# Media upload

> Upload a media file to a space (root workspace or subspace / app). Send the file as the raw request body with the appropriate Content-Type header (e.g. image/png, application/pdf, application/octet-stream). Pass the original filename as the `filename` query parameter.

- Source: https://www.taskade.com/docs/rest/media-upload
- Section: REST API
- Endpoint: `POST /medias/spaces/{spaceId}/upload` (API v1)
- Index of all pages: https://www.taskade.com/docs/llms.txt

## POST `/medias/spaces/{spaceId}/upload`

Upload a media file to a space (root workspace or subspace / app). Send the file as the raw request body with the appropriate Content-Type header (e.g. image/png, application/pdf, application/octet-stream). Pass the original filename as the `filename` query parameter.

Base URL: `https://www.taskade.com/api/v1`

### Authentication

Send a personal access token or an OAuth 2.0 access token as a bearer token. Create a personal access token at [taskade.com/settings/api](https://www.taskade.com/settings/api).

```http
Authorization: Bearer tskdp_your_token_here
```

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `filename` | query | `string` | Yes | Original filename of the uploaded file (e.g. "photo.png"). |
| `spaceId` | path | `string` | Yes | The space ID to upload media into (can be a root workspace or a subspace / app). |

### Example request

```bash
# Send the file as the raw request body. Swap application/octet-stream
# for the file's real type (image/png, application/zip, …) when you know it.
curl -X POST "https://www.taskade.com/api/v1/medias/spaces/<spaceId>/upload?filename=<filename>" \
  -H "Authorization: Bearer $TASKADE_TOKEN" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @/path/to/your-file
```

### Responses

#### `200` — Uploaded media file details

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `ok` | `boolean` | Yes | — |
| `item` | `object` | Yes | — |
| `item.mediaId` | `string` | Yes | The SpaceMedia record ID (UUID). |
| `item.fileId` | `string` | Yes | The file ID used in S3 (e.g. "space-files/uuid"). |
| `item.kind` | `string` | Yes | Detected media kind (image, video, audio, document, etc.). |
| `item.downloadUrl` | `string` | Yes | Public URL to download the uploaded file. |

A nested field marked Yes is required whenever its parent object is present.

#### `4XX` — Error description

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `ok` | `boolean` | Yes | — |
| `message` | `string` | Yes | — |
| `code` | `string` | Yes | — |
| `statusMessage` | `string` | Yes | — |
| `featureId` | `string` | No | The plan entitlement the request needs, set on some PAYMENT_REQUIRED errors. |

### Errors

Errors return an `ok: false` envelope. Rate limiting returns `429` with `x-rate-limit-limit`, `x-rate-limit-remaining` and `x-rate-limit-reset` headers. `x-rate-limit-reset` is the number of seconds until the window reopens. `Retry-After` is not sent, so schedule retries from `x-rate-limit-reset`; retrying sooner will fail again.

```json
{ "ok": false, "message": "Unauthorized", "code": "UNAUTHORIZED" }
```

Rate limits for this version: enforced per endpoint; ceilings are not published and can change without notice. A `429` response carries `x-rate-limit-limit`, `x-rate-limit-remaining`, and `x-rate-limit-reset` (seconds until the window reopens). Back off from those headers rather than hardcoding numbers. Bundle import and export run under tighter limits than regular reads and writes, so schedule bulk transfers accordingly. See [Rate limits](https://www.taskade.com/docs/getting-started/rest-api-guide#rate-limits).
