Upload File

Upload a file directly to create a knowledge item. This endpoint handles file uploads using multipart/form-data and automatically processes the file into searchable knowledge.

Endpoint

POST /v1/workspaces/{workspaceId}/knowledge/file

Authentication

This endpoint requires a Bearer token obtained through the two-step authentication flow:

  1. First, generate an access token using your API key
  2. Use that access token in the Authorization header

See Authentication for the complete authentication flow.

Required Headers:

  • Authorization: Bearer {access_token} - The JWT access token (NOT your raw API key)
  • organizationid: {your-organization-id} - Your organization ID

Path Parameters

ParameterTypeRequiredDescription
workspaceIdstring (UUID)YesThe ID of your workspace

Request Body

This endpoint uses multipart/form-data encoding to handle file uploads.

FieldTypeRequiredDescription
fileFileYesThe file to upload (binary data)
titlestringNoOptional title for the knowledge item
descriptionstringNoOptional description for the knowledge item
languagestringNoOptional language code (e.g., 'en', 'es', 'fr')
statusstringNoProcessing mode: BACKGROUND_START for async. Default: synchronous

Supported File Types

Documents

  • application/pdf - PDF documents
  • application/msword - Word documents (.doc)
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document - Word documents (.docx)
  • text/plain - Text files
  • text/markdown - Markdown files

Spreadsheets

  • application/vnd.ms-excel - Excel files (.xls)
  • application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - Excel files (.xlsx)
  • text/csv - CSV files

Images

  • image/jpeg - JPEG images
  • image/png - PNG images
  • image/gif - GIF images
  • image/webp - WebP images

Media

  • video/mp4 - MP4 videos
  • audio/mpeg - MP3 audio
  • audio/wav - WAV audio

Archives

  • application/zip - ZIP archives
  • application/x-zip-compressed - ZIP archives (alternate)

Data

  • application/json - JSON files

Response

Success Response

Status Code: 200 OK

Body:

{
  "uploadFileMetadataId": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Product Documentation",
  "description": "Complete product documentation",
  "language": "en",
  "status": "BACKGROUND_START"
}
FieldTypeDescription
uploadFileMetadataIdstring (UUID)Unique identifier to track the upload and retrieve the knowledge item
titlestringTitle provided in the request
descriptionstringDescription provided in the request
languagestringLanguage code provided in the request
statusstringProcessing mode from request

Important: The uploadFileMetadataId is returned immediately after the file is uploaded. Use this ID to track the processing status and retrieve the created knowledge item.

Tracking Upload Progress

After uploading a file, you can track its processing status using a two-step approach:

Step 1: Get the Knowledge ID

Use the uploadFileMetadataId from the upload response to retrieve the knowledge item ID:

curl -X GET \
  'https://api.sharely.ai/v1/workspaces/your-workspace-id/upload-file-metadata/550e8400-e29b-41d4-a716-446655440000' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'organizationid: your-organization-id'

Response (while processing):

{
  "knowledge": null,
  "spaceKnowledge": null
}

Response (after knowledge is created):

{
  "knowledge": {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "metadata": {
      "title": "Product Documentation",
      "type": "FILE"
    }
  },
  "spaceKnowledge": null
}

Note: The knowledge field will be null until the background processing creates the knowledge item. Poll this endpoint until knowledge is not null.

Step 2: Check Processing Status

Once you have the knowledge ID, check the current processing status:

curl -X GET \
  'https://api.sharely.ai/v1/workspaces/your-workspace-id/knowledge/123e4567-e89b-12d3-a456-426614174000' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'organizationid: your-organization-id'

Response:

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "type": "FILE",
  "content": "workspaces/your-workspace-id/knowledge/pdf/product-documentation.pdf",
  "status": "COMPLETED",
  "metadata": {
    "title": "Product Documentation",
    "description": "Complete product documentation"
  },
  "createdAt": "2025-01-15T10:30:00.000Z"
}

Status Values

StatusDescription
BACKGROUND_STARTFile is being processed (extraction, indexing)
COMPLETEDProcessing finished successfully
BACKGROUND_ERRORProcessing failed - check file format or contact support

Polling Example

Here's a complete example that uploads a file and polls for completion:

async function uploadAndWaitForCompletion(accessToken, file, options = {}) {
  // Step 1: Upload the file
  const formData = new FormData();
  formData.append('file', file);
  formData.append('title', options.title || file.name);
  formData.append('status', 'BACKGROUND_START');
 
  const uploadResponse = await fetch(
    `${API_BASE_URL}/v1/workspaces/${WORKSPACE_ID}/knowledge/file`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'organizationid': ORGANIZATION_ID
      },
      body: formData
    }
  );
 
  const { uploadFileMetadataId } = await uploadResponse.json();
 
  // Step 2: Poll until knowledge ID is available
  let knowledgeId = null;
  const maxMetadataAttempts = 60;
 
  for (let attempt = 0; attempt < maxMetadataAttempts; attempt++) {
    const metadataResponse = await fetch(
      `${API_BASE_URL}/v1/workspaces/${WORKSPACE_ID}/upload-file-metadata/${uploadFileMetadataId}`,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'organizationid': ORGANIZATION_ID
        }
      }
    );
 
    const { knowledge } = await metadataResponse.json();
 
    if (knowledge?.id) {
      knowledgeId = knowledge.id;
      break;
    }
 
    // Wait 5 seconds before next poll
    await new Promise(resolve => setTimeout(resolve, 5000));
  }
 
  if (!knowledgeId) {
    throw new Error('Timed out waiting for knowledge to be created');
  }
 
  // Step 3: Poll for processing completion
  const maxStatusAttempts = 60;
  for (let attempt = 0; attempt < maxStatusAttempts; attempt++) {
    const statusResponse = await fetch(
      `${API_BASE_URL}/v1/workspaces/${WORKSPACE_ID}/knowledge/${knowledgeId}`,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'organizationid': ORGANIZATION_ID
        }
      }
    );
 
    const knowledgeItem = await statusResponse.json();
 
    if (knowledgeItem.status === 'COMPLETED') {
      return knowledgeItem;
    }
 
    if (knowledgeItem.status === 'BACKGROUND_ERROR') {
      throw new Error(`File processing failed: ${knowledgeItem.messageError || 'Unknown error'}`);
    }
 
    // Wait 5 seconds before next poll
    await new Promise(resolve => setTimeout(resolve, 5000));
  }
 
  throw new Error('Timed out waiting for processing to complete');
}
 
// Usage
const accessToken = await getAccessToken();
const result = await uploadAndWaitForCompletion(accessToken, file, {
  title: 'My Document'
});
console.log('Upload complete:', result.id);
import time
import requests
 
def upload_and_wait_for_completion(access_token, file_path, title=None, timeout=300):
    headers = {
        'Authorization': f'Bearer {access_token}',
        'organizationid': ORGANIZATION_ID
    }
 
    # Step 1: Upload the file
    with open(file_path, 'rb') as f:
        files = {'file': f}
        data = {
            'title': title or file_path.split('/')[-1],
            'status': 'BACKGROUND_START'
        }
 
        upload_response = requests.post(
            f'{API_BASE_URL}/v1/workspaces/{WORKSPACE_ID}/knowledge/file',
            headers=headers,
            files=files,
            data=data
        )
 
    upload_file_metadata_id = upload_response.json()['uploadFileMetadataId']
 
    # Step 2: Poll until knowledge ID is available
    knowledge_id = None
    start_time = time.time()
 
    while time.time() - start_time < timeout:
        metadata_response = requests.get(
            f'{API_BASE_URL}/v1/workspaces/{WORKSPACE_ID}/upload-file-metadata/{upload_file_metadata_id}',
            headers=headers
        )
        knowledge = metadata_response.json().get('knowledge')
 
        if knowledge and knowledge.get('id'):
            knowledge_id = knowledge['id']
            break
 
        time.sleep(5)  # Wait 5 seconds before next poll
 
    if not knowledge_id:
        raise Exception('Timed out waiting for knowledge to be created')
 
    # Step 3: Poll for processing completion
    while time.time() - start_time < timeout:
        status_response = requests.get(
            f'{API_BASE_URL}/v1/workspaces/{WORKSPACE_ID}/knowledge/{knowledge_id}',
            headers=headers
        )
        knowledge_item = status_response.json()
 
        if knowledge_item['status'] == 'COMPLETED':
            return knowledge_item
 
        if knowledge_item['status'] == 'BACKGROUND_ERROR':
            error_msg = knowledge_item.get('messageError', 'Unknown error')
            raise Exception(f'File processing failed: {error_msg}')
 
        time.sleep(5)  # Wait 5 seconds before next poll
 
    raise Exception('Timed out waiting for processing to complete')
 
# Usage
access_token = get_access_token()
result = upload_and_wait_for_completion(access_token, './document.pdf', title='My Document')
print(f"Upload complete: {result['id']}")

Upload Workflow

This endpoint provides a simple, single-step file upload process using multipart/form-data.

Direct Upload (Single Request)

Upload a file and create the knowledge item in one request:

# Step 1: Generate access token (do this once, reuse for multiple requests)
ACCESS_TOKEN=$(curl -s -X POST \
  'https://api.sharely.ai/workspaces/your-workspace-id/generate-access-key-token' \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: sk-sharely-your-api-key' \
  -d '{}' | jq -r '.token')
 
# Step 2: Upload the file using the access token
curl -X POST \
  'https://api.sharely.ai/v1/workspaces/your-workspace-id/knowledge/file' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'organizationid: your-organization-id' \
  -F 'file=@/path/to/product-documentation.pdf' \
  -F 'title=Product Documentation' \
  -F 'status=BACKGROUND_START'

Response:

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "knowledgeId": "123e4567-e89b-12d3-a456-426614174000",
  "title": "Product Documentation",
  "type": "FILE",
  "status": "PROCESSING"
}

With Description and Language

Include optional description and language with your upload:

# Using the access token from Step 1 above
curl -X POST \
  'https://api.sharely.ai/v1/workspaces/your-workspace-id/knowledge/file' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'organizationid: your-organization-id' \
  -F 'file=@/path/to/document.pdf' \
  -F 'title=Technical Specification' \
  -F 'description=Complete technical specification for version 2.0' \
  -F 'language=en' \
  -F 'status=BACKGROUND_START'

Complete Example

Here's a complete example in different languages:

cURL

# Step 1: Generate access token
ACCESS_TOKEN=$(curl -s -X POST \
  'https://api.sharely.ai/workspaces/your-workspace-id/generate-access-key-token' \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: sk-sharely-your-api-key' \
  -d '{}' | jq -r '.token')
 
# Step 2: Upload a file and create knowledge item
curl -X POST \
  'https://api.sharely.ai/v1/workspaces/your-workspace-id/knowledge/file' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'organizationid: your-organization-id' \
  -F 'file=@./document.pdf' \
  -F 'title=My Document' \
  -F 'description=Documentation for my project' \
  -F 'language=en' \
  -F 'status=BACKGROUND_START'

JavaScript/TypeScript

const API_KEY = 'sk-sharely-your-api-key';
const WORKSPACE_ID = 'your-workspace-id';
const ORGANIZATION_ID = 'your-organization-id';
const API_BASE_URL = 'https://api.sharely.ai';
 
// Step 1: Generate access token
async function getAccessToken() {
  const response = await fetch(
    `${API_BASE_URL}/workspaces/${WORKSPACE_ID}/generate-access-key-token`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': API_KEY
      },
      body: JSON.stringify({})
    }
  );
 
  if (!response.ok) {
    throw new Error(`Failed to get access token: ${response.statusText}`);
  }
 
  const data = await response.json();
  return data.token;
}
 
// Step 2: Upload file using access token
async function uploadFile(accessToken, file, options = {}) {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('title', options.title || file.name);
 
  if (options.description) {
    formData.append('description', options.description);
  }
 
  if (options.language) {
    formData.append('language', options.language);
  }
 
  formData.append('status', 'BACKGROUND_START');
 
  const response = await fetch(
    `${API_BASE_URL}/v1/workspaces/${WORKSPACE_ID}/knowledge/file`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'organizationid': ORGANIZATION_ID
      },
      body: formData
    }
  );
 
  if (!response.ok) {
    throw new Error(`Upload failed: ${response.statusText}`);
  }
 
  return await response.json();
}
 
// Usage
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
 
// Get token and upload
const accessToken = await getAccessToken();
const result = await uploadFile(accessToken, file, {
  title: 'My Document',
  description: 'Documentation for my project',
  language: 'en'
});
 
console.log('Knowledge created:', result.id);

Python

import requests
 
API_KEY = 'sk-sharely-your-api-key'
WORKSPACE_ID = 'your-workspace-id'
ORGANIZATION_ID = 'your-organization-id'
API_BASE_URL = 'https://api.sharely.ai'
 
# Step 1: Generate access token
def get_access_token():
    response = requests.post(
        f'{API_BASE_URL}/workspaces/{WORKSPACE_ID}/generate-access-key-token',
        headers={
            'Content-Type': 'application/json',
            'x-api-key': API_KEY
        },
        json={}
    )
    response.raise_for_status()
    return response.json()['token']
 
# Step 2: Upload file using access token
def upload_file(access_token, file_path, title=None, description=None, language=None):
    # Prepare the multipart form data
    files = {
        'file': open(file_path, 'rb')
    }
 
    data = {
        'title': title or file_path.split('/')[-1],
        'status': 'BACKGROUND_START'
    }
 
    if description:
        data['description'] = description
 
    if language:
        data['language'] = language
 
    # Upload the file
    response = requests.post(
        f'{API_BASE_URL}/v1/workspaces/{WORKSPACE_ID}/knowledge/file',
        headers={
            'Authorization': f'Bearer {access_token}',
            'organizationid': ORGANIZATION_ID
        },
        files=files,
        data=data
    )
 
    response.raise_for_status()
    return response.json()
 
# Usage
access_token = get_access_token()
result = upload_file(
    access_token,
    './document.pdf',
    title='My Document',
    description='Documentation for my project',
    language='en'
)
print(f"Knowledge created: {result['id']}")

Error Responses

400 Bad Request

Missing or invalid request parameters:

{
  "error": "Bad Request",
  "message": "Missing required field: fileName"
}

Or unsupported file type:

{
  "error": "Bad Request",
  "message": "Unsupported file type"
}

401 Unauthorized

Invalid or missing API key:

{
  "error": "Unauthorized",
  "message": "Invalid or missing API key"
}

403 Forbidden

Insufficient permissions or storage quota exceeded:

{
  "error": "Forbidden",
  "message": "Storage quota exceeded"
}

500 Internal Server Error

Failed to generate signed URL:

{
  "error": "Internal Server Error",
  "message": "Failed to generate upload URL"
}

Best Practices

Use Async Processing for Large Files

  • For files larger than 10MB, set status: "BACKGROUND_START"
  • This prevents timeout errors during processing
  • Poll the knowledge status to check processing completion

Set Clear Titles

  • Always provide a descriptive title
  • If omitted, the filename will be used
  • Clear titles improve searchability

Include Description and Language

  • Add descriptions for better context and searchability
  • Specify the language code for multilingual content
  • Use clear, concise descriptions that explain the document's purpose

Error Handling

try {
  const response = await uploadFile(file);
  console.log('Upload successful:', response.id);
} catch (error) {
  if (error.status === 403) {
    console.error('Storage quota exceeded');
  } else if (error.status === 400) {
    console.error('Invalid file type or parameters');
  } else if (error.status === 413) {
    console.error('File too large');
  } else {
    console.error('Upload failed:', error.message);
  }
}

Check File Size Limits

  • Verify file size before upload
  • Check your workspace plan for limits
  • Split large documents if necessary

Notes

Direct Upload

  • This endpoint provides a single-step upload process
  • Files are uploaded using multipart/form-data
  • Processing begins immediately after upload completes

File Size Limits

  • Check your workspace plan for file size limits
  • Free tier: Up to 10MB per file
  • Pro tier: Up to 50MB per file
  • Enterprise plans support larger files
  • Contact support for custom limits

Processing Time

  • Small files (< 10MB): Typically under 1 minute
  • Medium files (10-50MB): 1-5 minutes
  • Large files (> 50MB): 5+ minutes
  • Use status: "BACKGROUND_START" for async processing

Content Extraction

  • PDFs: Text and embedded images are extracted
  • Office docs: Full content extraction with formatting preserved
  • Images: OCR is applied to extract text
  • Archives: Contents are extracted and processed individually

Related Endpoints