---
title: "Initialize a file upload"
method: POST
path: "/workspaces/{workspaceId}/consultations/{consultationId}/uploaded-files"
tags: ["uploaded-files"]
---

# Initialize a file upload

`POST /workspaces/{workspaceId}/consultations/{consultationId}/uploaded-files`

## Request

- Base URL: `https://vetnio.com/api/v2`
- URL: `https://vetnio.com/api/v2/workspaces/{workspaceId}/consultations/{consultationId}/uploaded-files`
- Auth: HTTP bearer

Reserves a slot for a new file in the consultation and returns a short-lived presigned S3 URL. Three-step flow:

1. Call this endpoint to get `uploadUrl` and `fileName`.
2. `PUT` the file bytes directly to `uploadUrl`.
3. Call `finalizeUpload` with the same `fileName` to start content extraction, then poll `GET .../uploaded-files` until the status is `analyzed` or `failed`.

De-duplication is by SHA-256: a second `initUpload` with the same `sha256` in the same consultation returns `409 CONFLICT`, including when the first upload was never completed (delete that file first to retry). The slot is created immediately with status `uploaded`. If URL generation fails after the slot is created, the slot is removed so the client can safely retry. Returns `403` for archived consultations.

## Path parameters

- `workspaceId` string, required: Opaque identifier of the Vetnio workspace (veterinary clinic) that owns the resource. Scopes every request — callers can only access files under workspaces they are a member of.
- `consultationId` string, required: Identifier of the consultation (a single veterinary visit) the file is attached to. The consultation must belong to the same workspace as the authenticated caller.

## Request body

- object
  - `originalFileName` string, required: The file name as it was on the uploader's device. Shown to users in the UI. May contain characters that are unsafe for object-storage keys, which is why it is never used as a storage identifier.
  - `sha256` string, required: Hexadecimal SHA-256 digest (64 characters) of the exact bytes you will upload, computed client-side. Used only for de-duplication: a consultation cannot hold two files with the same digest, so a second `initUpload` with the same value returns `409 CONFLICT`. Vetnio does not verify that the uploaded bytes match it.
  - `mimeType` 'application/pdf' | 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif' | 'text/csv' | 'text/plain' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | 'application/vnd.ms-excel', required: MIME type of the file. Restricted to the formats Vetnio can analyze: PDF, images (PNG / JPEG / WebP / GIF), plain text, CSV, Word (.docx), and Excel (.xlsx / .xls). Other values are rejected with `400`. Not checked against the uploaded bytes, so send the real type: it decides how the content is extracted.
  - `sizeBytes` integer, required: Exact size of the file in bytes. `finalizeUpload` compares it with the size of the uploaded object and returns `412` until they match, so it must equal the number of bytes you `PUT`. No maximum is enforced by the API.

## Response `200`

Upload slot reserved; use `uploadUrl` to send the bytes and `fileName` to finalize.

- UploadedFileInitResult: Carries the presigned URL the client must `PUT` to, plus the `fileName` identifier to use when calling `finalizeUpload`.
  - `uploadUrl` string, uri, required: Presigned S3 URL valid for 1 hour. `PUT` the raw file bytes (no multipart form) to this URL before it expires. If it lapses, delete this file and call `initUpload` again; a second `initUpload` with the same `sha256` returns `409` while this slot exists.
  - `s3Key` string, required: Internal S3 object key where the file will live. Exposed for debugging only — clients should not depend on its format or attempt to access S3 directly with it.
  - `fileName` string, required: Stable internal identifier for the file — a server-generated UUID returned by `initUpload`. Used in every subsequent endpoint (finalize, download, delete). Not intended for display; use `originalFileName` for that.

## Other responses

- `400`: Invalid input data
- `401`: Authorization not provided
- `403`: Insufficient access
- `500`: Internal server error

---

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