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

# Upload URLs

> Understand how pre-signed URLs work for secure file uploads

# Upload URLs

Upload URLs are secure, time-limited URLs that allow you to upload files directly to Sterndesk's storage without routing data through our API servers.

## Why Direct-to-Storage Uploads?

Sterndesk uses direct-to-storage uploads rather than routing files through our API servers. This architecture provides several advantages:

* **Long-running uploads** — Slow or unstable connections can complete uploads without timing out API requests
* **Larger file support** — Direct storage uploads handle large files more efficiently than API servers
* **Dedicated bandwidth** — Upload traffic goes directly to storage infrastructure, keeping API servers responsive for other operations
* **Reliability** — Storage services are optimized for handling file transfers with built-in retry and resumption capabilities

## How Upload URLs Work

When you create an upload through an [Upload Collector](/concepts/03-collectors#upload-collectors), Sterndesk generates a unique pre-signed URL for each file you want to upload. These URLs are:

* **Secure** — Each URL contains cryptographic signatures that authorize the upload
* **Time-limited** — URLs expire after a configurable duration (up to 1 hour)
* **File-specific** — Each file in an upload gets its own dedicated URL

## Pre-signed URLs

Sterndesk uses [AWS S3 pre-signed URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html) to enable direct uploads to storage. You don't need to understand the technical details of how AWS generates these signatures—simply use the URL as provided.

The pre-signed URL encodes:

* The target storage location
* The expected file size
* A cryptographic signature for authorization
* An expiration timestamp

## Creating Upload URLs

To get upload URLs, call the Create Upload endpoint with your upload collector ID and file specifications:

```bash theme={null}
curl -X POST "https://api.sterndesk.com/r/uploads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_collector_id": "usrc_abc123...",
    "files": [
      {"size_bytes": 1048576},
      {"size_bytes": 2097152}
    ],
    "upload_expiration": "600s"
  }'
```

The response contains a pre-signed URL for each file:

```json theme={null}
{
  "pre_signs": [
    {
      "url": "https://storage.example.com/...",
      "strategy": "UPLOAD_STRATEGY_PUT"
    },
    {
      "url": "https://storage.example.com/...",
      "strategy": "UPLOAD_STRATEGY_PUT"
    }
  ]
}
```

## Uploading a File

Each file must be uploaded using an HTTP `PUT` request to its corresponding pre-signed URL:

```bash theme={null}
curl -X PUT "PRE_SIGNED_URL" \
  --data-binary @/path/to/your/file.pdf
```

<Warning>
  Upload all files in the order they were specified when creating the upload. The first pre-signed URL corresponds to the first file specification, and so on.
</Warning>

## File Size Declaration

When creating an upload, you **must** declare the exact size of each file in bytes using the `size_bytes` field. This is critical because:

1. **Pre-signed URLs are size-locked** — The URL signature includes the expected file size. Uploading a file of a different size will fail.
2. **Storage allocation** — Sterndesk pre-allocates storage and validates that uploads don't exceed limits.
3. **Security** — Size locking prevents abuse by ensuring only the declared content can be uploaded.

### How to Determine File Size

Get the file size before creating the upload:

```javascript theme={null}
// Browser
const fileSize = file.size;

// Node.js
const fs = require('fs');
const stats = fs.statSync('/path/to/file.pdf');
const fileSize = stats.size;
```

```python theme={null}
import os

file_size = os.path.getsize('/path/to/file.pdf')
```

## Common Errors

### HTTP 403 Forbidden — Size Mismatch

**Cause:** The file you're uploading is a different size than what was declared when creating the upload.

**Solution:** Ensure the `size_bytes` value matches the exact file size in bytes. Re-create the upload with the correct size if needed.

```
SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided.
```

### HTTP 403 Forbidden — Expired URL

**Cause:** The pre-signed URL has expired. URLs are valid for the duration specified in `upload_expiration` (maximum 1 hour).

**Solution:** Create a new upload to get fresh pre-signed URLs. Upload files promptly after receiving URLs.

### HTTP 403 Forbidden — Access Denied

**Cause:** The URL has already been used to upload a file, or the signature is otherwise invalid.

**Solution:** Each pre-signed URL can only be used once. If you need to re-upload a file, create a new upload.

### Upload Status: EXPIRED

**Cause:** The upload window closed before all files were transferred.

**Solution:** Ensure all files are uploaded within the expiration window. For large files or slow connections, request a longer `upload_expiration` duration (up to 1 hour).

## Best Practices

<CardGroup cols={2}>
  <Card title="Verify file sizes" icon="ruler">
    Always measure file size immediately before creating an upload to ensure accuracy.
  </Card>

  <Card title="Upload promptly" icon="clock">
    Start uploading as soon as you receive pre-signed URLs to avoid expiration.
  </Card>

  <Card title="Handle errors gracefully" icon="rotate">
    Implement retry logic that creates new uploads on failure rather than reusing expired URLs.
  </Card>

  <Card title="Upload in order" icon="list-ol">
    Upload files in the same order as their specifications to maintain correct file-to-URL mapping.
  </Card>
</CardGroup>

## API Reference

For complete details on creating uploads and managing upload URLs, see the [Upload API Reference](https://api.eu.sterndesk.com/r/#tag/upload).

## Next Steps

<CardGroup cols={2}>
  <Card title="Collectors" icon="inbox" href="/concepts/03-collectors">
    Learn how to create and manage Upload Collectors
  </Card>

  <Card title="Extraction Schemas" icon="brackets-curly" href="/concepts/02-extraction-schemas">
    Define how uploaded data is extracted and structured
  </Card>
</CardGroup>
