---
title: "Begin an asset upload"
method: POST
path: "/websiteAssets"
tags: ["Assets"]
---

# Begin an asset upload

`POST /websiteAssets`

## Request

- Base URL: `https://api.cactal.ai/v1`
- URL: `https://api.cactal.ai/v1/websiteAssets`
- Auth: HTTP bearer

Step 1 of the two-step upload: validates `kind`, `mimeType`, and `byteSize`, creates the asset as `pending`, and returns presigned upload instructions. Include a one-line `description` (and `category` for images) so the asset can be found later. PUT the raw bytes to `upload.url` with exactly `upload.headers` within 15 minutes, then finalize. `image` accepts PNG, JPEG, WebP, GIF, AVIF, SVG, BMP, and ICO up to 10 MiB; `file` accepts documents, fonts, audio, video, images, ZIP, `.riv`, and `.lottie` up to 100 MiB. Unfinalized uploads are cleaned up after about an hour.

## Request body

- object
  - `websiteId` string, required
  - `kind` 'image' | 'file', required
  - `filename` string, required
  - `mimeType` string, required
  - `byteSize` integer, required
  - `description` string: One sentence: subject, visible text, style, intended use.
  - `category` 'logo' | 'icon' | 'photo' | 'illustration' | 'screenshot' | 'graphic' | 'background' | 'other': What the image is. Images only.
  - `listed` boolean: Pass `false` to keep the asset out of lists until the website uses it; it is then never described automatically. Cactal sets this for images attached to agent messages.

## Response `200`

The pending asset id and presigned upload instructions. Next: PUT the file bytes to `upload.url`, then call `websiteAssets.finalize` with `assetId`.

- object
  - `assetId` string, required: Id of the pending asset. After uploading the bytes, pass it to websiteAssets.finalize.
  - `upload` object, required: Presigned instructions for uploading the file bytes.
    - `url` string, required: Presigned URL to PUT the raw file bytes to.
    - `method` 'PUT', required: HTTP method to use against `url`.
    - `headers` object, required: Headers that must be sent exactly as given on the PUT because the signature is bound to the declared `Content-Type` and `Content-Length`.
    - `expiresAt` string, date-time, required: When the presigned URL stops working (15 minutes after issue).

## Other responses

- `400`: Validation failed. The response `message` names the first invalid field.
- `401`: Missing, invalid, expired, or revoked API key.
- `403`: The authenticated principal lacks the required capability.
- `404`: The resource does not exist or is outside the principal’s access scope.
- `429`: Rate limit exceeded. Retry after the number of seconds in the `Retry-After` header.

---

[API](https://skmtc.dev/cactal/apis/cactal-api.md) · [All operations](https://skmtc.dev/cactal/apis/cactal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cactal/apis/cactal-api/revisions/9cd0fb5eacdd?raw)
