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

# Create an upload

> Request a pre-signed URL to upload an image asset.

<Warning>
  Content API endpoints are currently in an open alpha and are subject to
  change.
</Warning>

Request a pre-signed URL to upload an image asset. Upload the file with an HTTP
`PUT` to the returned `presignedUrl` (sending the same `Content-Type` and
`Content-Length`), then call
[Complete an upload](/api-reference/complete-upload) to finalize the asset.

## Request

### Body

<ParamField body="contentType" type="string" required>
  The MIME type of the file to upload. Supported types are `image/jpeg`,
  `image/png`, `image/gif`, and `image/webp`.
</ParamField>

<ParamField body="contentLength" type="integer" required>
  The size of the file in bytes. Must be a positive integer no greater than
  4,000,000 bytes.
</ParamField>

## Response

### Success

<ResponseField name="success" type="boolean" required />

<ResponseField name="emailAssetId" type="string" required>
  The ID of the created asset. Pass this as `id` to
  [Complete an upload](/api-reference/complete-upload) once the file has been
  uploaded.
</ResponseField>

<ResponseField name="presignedUrl" type="string" required>
  The pre-signed URL to upload the file to with an HTTP `PUT` request. Send the
  same `Content-Type` and `Content-Length` used in the create request.
</ResponseField>

### Error

If the request body is invalid, or if `contentType` is unsupported, a
`400 Bad Request` is returned. The response may include a
`supportedContentTypes` array listing accepted MIME types.

If the upload exceeds the maximum allowed size, a `413 Payload Too Large` is
returned. The response may include a `maxBytes` field with the size limit.

If the upload limit is exceeded, a `429 Too Many Requests` is returned. The
response may include `maxUploads` and `windowHours` fields describing the
limit window.

If the API key is invalid (or content API is not enabled for your team), a
`401 Unauthorized` is returned.

<ResponseField name="success" type="boolean" required />

<ResponseField name="message" type="string" required>
  An error message describing what went wrong.
</ResponseField>

<ResponseField name="supportedContentTypes" type="string[]">
  Present when the request was rejected for an unsupported `contentType`. Lists
  the accepted MIME types.
</ResponseField>

<ResponseField name="maxBytes" type="integer">
  Present when the upload exceeds the size limit. The maximum allowed size in
  bytes.
</ResponseField>

<ResponseField name="maxUploads" type="integer">
  Present when the upload limit is exceeded. The maximum number of uploads
  allowed per window.
</ResponseField>

<ResponseField name="windowHours" type="integer">
  Present when the upload limit is exceeded. The number of hours in the upload
  limit window.
</ResponseField>

<ResponseExample>
  ```json Response theme={"dark"}
  {
    "success": true,
    "emailAssetId": "asset_01hxyz",
    "presignedUrl": "https://storage.example.com/upload?signature=..."
  }
  ```

  ```json Error response theme={"dark"}
  {
    "success": false,
    "message": "Unsupported contentType.",
    "supportedContentTypes": [
      "image/jpeg",
      "image/png",
      "image/gif",
      "image/webp"
    ]
  }
  ```

  ```json Upload limit exceeded theme={"dark"}
  {
    "success": false,
    "message": "Upload limit exceeded: max 50 uploads per 24 hours. Please contact support if you need to increase your upload limit.",
    "maxUploads": 50,
    "windowHours": 24
  }
  ```
</ResponseExample>
