---
title: "Get a presigned URL for a direct file upload"
method: POST
path: "/api/files/upload-url"
tags: ["Files"]
---

# Get a presigned URL for a direct file upload

`POST /api/files/upload-url`

## Request

- Base URL: `https://core-api.getaptly.com`
- URL: `https://core-api.getaptly.com/api/files/upload-url`
- Auth: one of:
  - API key in header `x-token`
  - API key in header `Authorization`
  - HTTP bearer
- TypeScript: <https://skmtc.dev/getaptly/apis/aptly-api/docs/api/files/upload-url/post.ts> (a function with typed inputs; its response is checked against the schema)

First step of the direct file-upload flow. Issues a presigned S3 POST
policy and records a pending upload.

**Uploading a file takes three steps:**

1. **Get a URL** — call this endpoint. It returns `{ fileId, url, fields }`.
2. **Upload directly to S3** — send a `multipart/form-data` `POST` to
   `url`. Include **every** key/value from `fields` as form fields first,
   then the file itself as the last field named `file`. The upload goes
   straight to S3; it does not pass through this API. A successful upload
   returns HTTP `204` from S3.
3. **Mark the upload complete** — call
   `POST /api/files/upload-complete` with the `fileId` from step 1. This
   records the file and returns its download URL.

The presigned policy enforces the content type and a 1 byte–50 MB size
range. Both `extension` and `contentType` must be on the accepted lists.

`attachEntityType` is `channel`, `aptlet`, `knowledge`, or `template`, and
`attachEntityId` is the channel id, aptlet uuid, knowledge doc id, or
communication template id respectively. The target must exist (scoped to your
company) or the request is rejected. The file is stored under that entity's
folder.

## Request body

- object
  - `name` string, required: Original file name.
  - `extension` string, required: File extension (lowercase, no dot). Must be an accepted extension.
  - `size` integer, required: File size in bytes. Must be 1–52428800 (50 MB).
  - `contentType` string, required: MIME type. Must be an accepted content type.
  - `attachEntityType` 'channel' | 'aptlet' | 'knowledge' | 'template', required: The kind of entity the file is attached to.
  - `attachEntityId` string, required: The channel id (`channel`), aptlet uuid (`aptlet`), knowledge doc id (`knowledge`), or communication template id (`template`) the file belongs to. Must exist within your company.

## Response `200`

Presigned upload policy.

- object
  - `fileId` string: ID to pass to `/api/files/upload-complete`.
  - `url` string: S3 endpoint to POST the multipart form to.
  - `fields` object: Form fields that must be included in the multipart POST (the file part goes last).

## Other responses

- `400`: Invalid or unsupported file, or missing required field.
- `401`: Invalid or missing credential.
- `404`: Target channel, aptlet, or knowledge doc not found.

---

[API](https://skmtc.dev/getaptly/apis/aptly-api.md) - [All operations](https://skmtc.dev/getaptly/apis/aptly-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getaptly/apis/aptly-api/revisions/75c367e0b8e5?raw)
