> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.fifteenth.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload Document

> Upload tax documents for AI-powered processing and extraction

## Overview

The Document Upload endpoint allows you to upload tax documents directly to a client's Fifteenth account. Documents are stored securely and can be organized by tax year with custom descriptions.

<Note>
  Documents are stored securely and can be retrieved using the document ID.
</Note>

## Supported Document Types

### Tax Forms

* **W-2**: Wage and tax statements
* **1099 Series**: 1099-INT, 1099-DIV, 1099-B, 1099-R, etc.
* **1098**: Mortgage interest statements
* **K-1**: Partnership, S-Corp, trust distributions
* **Prior Year Returns**: Previous tax returns

### Supporting Documents

* **Bank Statements**: Account summaries and transactions
* **Investment Statements**: Brokerage and retirement accounts
* **Receipts**: Business expenses, charitable donations
* **Legal Documents**: Divorce decrees, adoption papers
* **Business Records**: P\&L statements, balance sheets

### File Requirements

* **Formats**: PDF, PNG, JPG, JPEG, TIFF
* **Size Limit**: 25MB per file
* **Quality**: Minimum 300 DPI for optimal OCR
* **Pages**: Up to 50 pages per document

## Request

### Path Parameters

<ParamField path="account_id" type="number" required>
  The unique Fifteenth account identifier to upload documents to.

  **Format**: Numeric ID\
  **Example**: `12345`
</ParamField>

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token with your Partner API key
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `multipart/form-data` for file uploads
</ParamField>

### Form Data Parameters

<ParamField body="file" type="file" required>
  The tax document file to upload.

  **Formats**: PDF, PNG, JPG, JPEG, TIFF\
  **Size**: Maximum 25MB\
  **Quality**: 300 DPI recommended for best OCR results
</ParamField>

<ParamField body="tax_year" type="integer" required>
  Tax year this document relates to.

  **Range**: 2020-2025\
  **Example**: `2024`
</ParamField>

<ParamField body="description" type="string">
  Human-readable description of the document.

  **Max**: 500 characters\
  **Example**: "W-2 from ABC Corporation for 2024"
</ParamField>

## Response

### Success Response

<ResponseField name="id" type="number">
  Unique document identifier.
</ResponseField>

<ResponseField name="account_id" type="number">
  The account this document belongs to.
</ResponseField>

<ResponseField name="filename" type="string">
  Original filename of the uploaded document.
</ResponseField>

<ResponseField name="tax_year" type="integer">
  Tax year for this document.
</ResponseField>

<ResponseField name="description" type="string">
  Document description.
</ResponseField>

<ResponseField name="file_size" type="integer">
  File size in bytes.
</ResponseField>

<ResponseField name="file_type" type="string">
  MIME type of the uploaded file.
</ResponseField>

<ResponseField name="uploaded_at" type="string">
  ISO 8601 timestamp when document was uploaded.
</ResponseField>

## Examples

### Basic Document Upload

<CodeGroup>
  ```python Python theme={null}
  import requests

  account_id = 12345
  url = f"https://api.fifteenth.com/v1beta/accounts/{account_id}/documents"

  headers = {
      "Authorization": "Bearer 15th_your_api_key_here"
  }

  # Upload W-2 document
  with open('w2_2024.pdf', 'rb') as file:
      files = {'file': file}
      data = {
          'tax_year': '2024',
          'description': 'W-2 from ABC Corporation'
      }
      
      response = requests.post(url, headers=headers, files=files, data=data)
      document = response.json()
      
  print(f"Document uploaded: {document['id']}")
  print(f"Tax year: {document['tax_year']}")
  print(f"Description: {document['description']}")
  ```

  ```javascript JavaScript theme={null}
  const accountId = 12345;
  const url = `https://api.fifteenth.com/v1beta/accounts/${accountId}/documents`;

  const headers = {
      'Authorization': 'Bearer 15th_your_api_key_here'
  };

  // Create form data
  const formData = new FormData();
  formData.append('file', fileInput.files[0]); // From file input element
  formData.append('tax_year', '2024');
  formData.append('description', 'W-2 from ABC Corporation');

  const response = await fetch(url, {
      method: 'POST',
      headers: headers,
      body: formData
  });

  const document = await response.json();

  console.log(`Document uploaded: ${document.id}`);
  console.log(`Tax year: ${document.tax_year}`);
  console.log(`Description: ${document.description}`);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.fifteenth.com/v1beta/accounts/12345/documents" \
    -H "Authorization: Bearer 15th_your_api_key_here" \
    -F "file=@w2_2024.pdf" \
    -F "tax_year=2024" \
    -F "description=W-2 from ABC Corporation"
  ```
</CodeGroup>

### Multiple Document Upload

<CodeGroup>
  ```python Python theme={null}
  import requests
  import os
  from concurrent.futures import ThreadPoolExecutor

  def upload_document(account_id, file_path, doc_info):
      """Upload a single document"""
      url = f"https://api.fifteenth.com/v1beta/accounts/{account_id}/documents"
      headers = {"Authorization": "Bearer 15th_your_api_key_here"}
      
      with open(file_path, 'rb') as file:
          files = {'file': file}
          response = requests.post(url, headers=headers, files=files, data=doc_info)
          return response.json()

  # Define documents to upload
  documents = [
      {
          'file_path': 'w2_2024.pdf',
          'doc_info': {
              'tax_year': '2024',
              'description': 'W-2 from employer'
          }
      },
      {
          'file_path': '1099_div.pdf', 
          'doc_info': {
              'tax_year': '2024',
              'description': 'Dividend income from investments'
          }
      }
  ]

  # Upload documents in parallel
  with ThreadPoolExecutor(max_workers=3) as executor:
      futures = [
          executor.submit(upload_document, account_id, doc['file_path'], doc['doc_info'])
          for doc in documents
      ]
      
      results = [future.result() for future in futures]

  for result in results:
      print(f"Uploaded: {result['filename']} - ID: {result['id']}")
  ```

  ```javascript JavaScript theme={null}
  async function uploadDocument(accountId, file, docInfo) {
      const url = `https://api.fifteenth.com/v1beta/accounts/${accountId}/documents`;
      const headers = {
          'Authorization': 'Bearer 15th_your_api_key_here'
      };
      
      const formData = new FormData();
      formData.append('file', file);
      
      // Add document info to form data
      Object.keys(docInfo).forEach(key => {
          formData.append(key, docInfo[key]);
      });
      
      const response = await fetch(url, {
          method: 'POST',
          headers: headers,
          body: formData
      });
      
      return await response.json();
  }

  // Upload multiple documents
  const documentUploads = [
      {
          file: w2File, // File object from input
          docInfo: {
              tax_year: '2024',
              description: 'W-2 from employer'
          }
      },
      {
          file: dividendFile,
          docInfo: {
              tax_year: '2024', 
              description: 'Dividend income from investments'
          }
      }
  ];

  const uploadPromises = documentUploads.map(({file, docInfo}) => 
      uploadDocument(accountId, file, docInfo)
  );

  const results = await Promise.all(uploadPromises);

  results.forEach(result => {
      console.log(`Uploaded: ${result.filename} - ID: ${result.id}`);
  });
  ```
</CodeGroup>

## Response Examples

### Successful Upload Response

```json Response theme={null}
{
  "id": 12345,
  "account_id": 12345,
  "filename": "w2_2024.pdf",
  "tax_year": 2024,
  "description": "W-2 from ABC Corporation",
  "file_size": 245760,
  "file_type": "application/pdf",
  "uploaded_at": "2024-01-15T10:35:00Z"
}
```

## Error Responses

<ResponseField name="400 Bad Request" type="object">
  Invalid file or request parameters.

  ```json theme={null}
  {
    "error": {
      "code": "INVALID_FILE_TYPE",
      "message": "Unsupported file type. Supported formats: PDF, PNG, JPG, JPEG, TIFF",
      "details": {
        "provided_type": "application/msword",
        "supported_types": ["application/pdf", "image/png", "image/jpeg", "image/tiff"]
      }
    }
  }
  ```
</ResponseField>

<ResponseField name="413 Payload Too Large" type="object">
  File exceeds size limit.

  ```json theme={null}
  {
    "error": {
      "code": "FILE_TOO_LARGE",
      "message": "File size exceeds 25MB limit",
      "details": {
        "file_size_mb": 32.5,
        "max_size_mb": 25
      }
    }
  }
  ```
</ResponseField>

<ResponseField name="422 Unprocessable Entity" type="object">
  File cannot be processed (corrupted, encrypted, etc.).

  ```json theme={null}
  {
    "error": {
      "code": "UNPROCESSABLE_FILE",
      "message": "File appears to be corrupted or encrypted",
      "details": {
        "file_analysis": "PDF appears to be password protected",
        "suggestion": "Please upload an unencrypted version"
      }
    }
  }
  ```
</ResponseField>

## Best Practices

### File Optimization

<AccordionGroup>
  <Accordion title="Image Quality">
    For best results:

    * Use 300 DPI or higher resolution
    * Ensure good lighting and contrast
    * Avoid shadows and glare
    * Keep text straight and unrotated
  </Accordion>

  <Accordion title="PDF Guidelines">
    * Use text-based PDFs when possible (not scanned images)
    * Ensure PDFs are not password protected
    * Combine related pages into single documents
    * Compress large PDFs while maintaining quality
  </Accordion>

  <Accordion title="Batch Processing">
    * Upload related documents together
    * Use consistent naming conventions
    * Include clear descriptions for organization
  </Accordion>
</AccordionGroup>

### Error Handling

```python theme={null}
def robust_document_upload(account_id, file_path, doc_info, max_retries=3):
    """Upload document with retry logic"""
    url = f"https://api.fifteenth.com/v1beta/accounts/{account_id}/documents"
    headers = {"Authorization": "Bearer 15th_your_api_key_here"}
    
    for attempt in range(max_retries):
        try:
            with open(file_path, 'rb') as file:
                files = {'file': file}
                response = requests.post(url, headers=headers, files=files, data=doc_info)
                
                if response.status_code == 200:
                    return response.json()
                elif response.status_code == 413:
                    # File too large - don't retry
                    raise Exception(f"File too large: {response.json()['error']['message']}")
                elif response.status_code in [500, 502, 503]:
                    # Server error - retry
                    if attempt &lt; max_retries - 1:
                        time.sleep(2 ** attempt)  # Exponential backoff
                        continue
                    else:
                        raise Exception(f"Server error after {max_retries} attempts")
                else:
                    # Client error - don't retry
                    raise Exception(f"Client error: {response.json()['error']['message']}")
                    
        except requests.exceptions.RequestException as e:
            if attempt &lt; max_retries - 1:
                time.sleep(2 ** attempt)
                continue
            else:
                raise Exception(f"Network error after {max_retries} attempts: {str(e)}")
    
    raise Exception(f"Upload failed after {max_retries} attempts")
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Retrieve Document" icon="file" href="/endpoints/documents/retrieve">
    Get document details and metadata
  </Card>

  <Card title="List Documents" icon="list" href="/endpoints/documents/list">
    View all documents for an account
  </Card>

  <Card title="Project Status" icon="chart-line" href="/endpoints/projects/status">
    Monitor how documents contribute to tax projects
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    Learn about API keys and security best practices
  </Card>
</CardGroup>
