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

# Create Upload Slot

> Get a signed storage form to upload one recording

For a `direct_upload` row in state `draft`. Returns a short-lived form. Upload the file to `upload.url` yourself, then call [Confirm upload](https://docs.60db.ai/api-reference/call-analysis/confirm-upload.md). A new slot replaces an older unused one for the same row.

> [!NOTE]
>
> Needs `manage` on the analyzer.

## Request

### Headers

**Authorization** `string` *required*

Bearer token with your API key: `Bearer sk_...`

**Content-Type** `string` *required*

application/json

### Path

**batchId** `string` *required*

Batch ID (UUID)

**itemId** `string` *required*

Batch row ID (UUID). Get it from [List batch rows](https://docs.60db.ai/api-reference/call-analysis/list-batch-items.md)

### Body

**size\_bytes** `integer` *required*

File size in bytes, at least 1. Maximum 100 MiB

**content\_type** `string` *required*

`audio/mpeg`, `audio/wav`, `audio/x-wav`, `audio/mp4`, `audio/x-m4a`, `audio/ogg`, `audio/flac` or `audio/webm`. The file content must match

## Response

**201 Created**

```json theme={null}
{
  "success": true,
  "data": {
    "upload_id": "<upload-id>",
    "expires_at": "2026-11-12T09:15:00Z",
    "max_size_bytes": 104857600,
    "content_type": "audio/mpeg",
    "upload": {
      "method": "POST",
      "url": "https://<storage-host>/<bucket>",
      "fields": {
        "key": "<value>",
        "policy": "<value>"
      }
    }
  }
}
```

### Upload the file

Send a `multipart/form-data` `POST` to `upload.url`. Put every entry of `upload.fields` first, in order, then the file as the **last** field, named `file`. Do not send your `Authorization` header to storage.

```bash theme={null}
curl -X POST "$UPLOAD_URL" -F "key=..." -F "policy=..." -F "file=@one.mp3;type=audio/mpeg"
```

Storage answers `403` when the slot has expired and `400` when the size or type does not match what you asked for.

## Errors

Errors use `{ "success": false, "message": "...", "code": "..." }`. See the [overview](https://docs.60db.ai/api-reference/call-analysis/overview.md#errors) for the codes every route can return.

### This endpoint

| Status | Code | When |
| - | - | - |
| 400 | `INVALID_UPLOAD_REQUEST` | Size or type not allowed |
| 413 | `ASSET_TOO_LARGE` | `size_bytes` is over 100 MiB |
| 409 | `ITEM_NOT_VALID` | The row is invalid |
| 409 | `ASSET_ALREADY_VERIFIED` | This row already has a verified recording |
| 409 | `WRONG_SOURCE_KIND` | The batch is not `direct_upload` |
| 501 | `STORAGE_NOT_CONFIGURED` | Uploads are not available |

## Example

```bash cURL theme={null}
curl -X POST 'https://api.60db.ai/deep-analytics/imports/batches/9b7e6d5c-4b3a-4291-8f0e-1d2c3b4a5968/items/c1d2e3f4-a5b6-4c7d-9e8f-7a6b5c4d3e2f/upload-slot' \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"size_bytes": 1843200, "content_type": "audio/mpeg"}'
```

```javascript JavaScript theme={null}
const res = await fetch('https://api.60db.ai/deep-analytics/imports/batches/9b7e6d5c-4b3a-4291-8f0e-1d2c3b4a5968/items/c1d2e3f4-a5b6-4c7d-9e8f-7a6b5c4d3e2f/upload-slot', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer your-api-key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "size_bytes": 1843200,
    "content_type": "audio/mpeg"
  }),
});
const { success, data, message, code } = await res.json();
if (!success) throw new Error(`${code}: ${message}`);
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.