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

# Upload asset

> Upload a file to a chat and get a permanent CDN URL

Uploads a file to a chat's Assets and returns a permanent CDN URL. Use this for images, documents, audio, video, or any other file you want your app or workflow to reference by URL.

## Request

```
POST /api/client-app/assets/upload
```

### Headers

| Header          | Required | Description           |
| --------------- | -------- | --------------------- |
| `Authorization` | Yes      | `Bearer macaly_...`   |
| `Content-Type`  | Yes      | `multipart/form-data` |

### Form Data Parameters

| Field           | Type          | Required | Default           | Description                                                         |
| --------------- | ------------- | -------- | ----------------- | ------------------------------------------------------------------- |
| `chatId`        | string        | Yes      | -                 | The chat to upload the asset to                                     |
| `file`          | file          | Yes      | -                 | The file to upload. Max size 20 MB                                  |
| `folderPath`    | string        | No       | -                 | Slash-separated folder path, e.g. `images/products`                 |
| `createFolders` | string        | No       | `false`           | `"true"`, `"1"`, or `"yes"` creates missing folders in `folderPath` |
| `folderId`      | string        | No       | -                 | Existing folder ID. Do not send both `folderId` and `folderPath`    |
| `title`         | string        | No       | Original filename | Display title for the asset                                         |
| `metadata`      | string (JSON) | No       | -                 | JSON object stored alongside the asset                              |

<Note>
  Provide either `folderId` or `folderPath`, not both. Sending both returns a 400 error.
</Note>

### Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://www.macaly.com/api/client-app/assets/upload \
    -H "Authorization: Bearer macaly_abc123..." \
    -F "chatId=abc123def456" \
    -F "file=@./logo.png;type=image/png"
  ```

  ```javascript JavaScript theme={null}
  const form = new FormData();
  form.append('chatId', 'abc123def456');
  form.append('file', fileBlob, 'logo.png');

  const response = await fetch('https://www.macaly.com/api/client-app/assets/upload', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer macaly_abc123...',
    },
    body: form,
  });

  const data = await response.json();
  console.log(data.asset.url);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://www.macaly.com/api/client-app/assets/upload',
      headers={'Authorization': 'Bearer macaly_abc123...'},
      data={'chatId': 'abc123def456'},
      files={'file': ('logo.png', open('logo.png', 'rb'), 'image/png')},
  )

  data = response.json()
  print(data['asset']['url'])
  ```
</CodeGroup>

### Uploading into a folder

Use `folderPath` for a human-readable folder structure, and set `createFolders=true` to create any missing folders along the path:

```bash cURL theme={null}
curl -X POST https://www.macaly.com/api/client-app/assets/upload \
  -H "Authorization: Bearer macaly_abc123..." \
  -F "chatId=abc123def456" \
  -F "folderPath=images/products" \
  -F "createFolders=true" \
  -F "title=Product hero image" \
  -F "file=@./hero.png;type=image/png"
```

## Response

```json theme={null}
{
  "success": true,
  "asset": {
    "assetId": "asset_123",
    "url": "https://cdn.macaly.app/user/chat/key/logo.png",
    "storageKey": "user/chat/key/logo.png",
    "contentType": "image/png",
    "fileSize": 12345,
    "type": "image",
    "folderId": "folder_123"
  }
}
```

| Field               | Type           | Description                                   |
| ------------------- | -------------- | --------------------------------------------- |
| `asset.assetId`     | string         | Unique identifier for the asset               |
| `asset.url`         | string         | Permanent CDN URL for the uploaded file       |
| `asset.storageKey`  | string         | Internal storage path                         |
| `asset.contentType` | string         | MIME type of the uploaded file                |
| `asset.fileSize`    | number         | File size in bytes                            |
| `asset.type`        | string         | Inferred asset type, e.g. `image`, `document` |
| `asset.folderId`    | string \| null | Folder the asset was placed in, if any        |

## Status Codes

| Status | Description                                                                                       |
| ------ | ------------------------------------------------------------------------------------------------- |
| 200    | Success - Asset uploaded                                                                          |
| 400    | Missing `chatId` or `file`, invalid `metadata` JSON, or both `folderId` and `folderPath` provided |
| 401    | Invalid or missing API key                                                                        |
| 403    | API key does not have access to this chat                                                         |
| 404    | Folder not found                                                                                  |
| 413    | File exceeds the 20 MB upload limit                                                               |
| 500    | Server error                                                                                      |

## Folder rules

* Folder paths are split on `/`, trimmed, and empty segments are ignored.
* Maximum depth is 8 folders.
* Each folder name can be up to 255 characters.
* Folder matching is case-insensitive within the same parent.
* If `folderPath` doesn't exist and `createFolders` isn't `true`, the upload fails with a 404.

## Good to know

* This endpoint uses the same `macaly_...` API key as the rest of the API, even though the URL starts with `/api/client-app/`. No separate token is needed.
* The chat referenced by `chatId` must belong to the same team as your API key.
* Uploaded assets get a permanent CDN URL you can reference in [Create chat](/en/api/create-chat) or [Publish message](/en/api/publish-message) as an attachment, or use anywhere outside Macaly.
