---
title: "Register a file upload"
method: POST
path: "/files"
tags: ["files"]
---

# Register a file upload

`POST /files`

Registers an image or video in the org's file library and returns a presigned S3 URL to upload the bytes to. PUT the raw file to upload_url with the declared Content-Type and Content-Length headers before the URL expires, then call the complete endpoint to make it ready. The registered file has source=upload. Uploads are capped per file and per org by total size.

## Request body

- FileCreateRequest — Registers an upload and requests a presigned PUT URL.
  - `$schema` string, uri — A URL to the JSON Schema for this object.
  - `filename` string, required — Display name for the file (also its name on the phone).
  - `mime_type` string, required — MIME type of the upload; must be an allowed image or video type.
  - `size_bytes` integer, required — Exact size of the upload in bytes, up to 1 GiB; the presigned URL pins it.

## Response `201`

Created

- FileUploadResponse — The registered file and its presigned upload URL.
  - `$schema` string, uri — A URL to the JSON Schema for this object.
  - `file` FileSummary, required — One file in the org's library.
    - `attachment_url` string — Short-lived signed URL that downloads the file as an attachment under its name. Present only for ready files.
    - `bytes_transferred` integer — Bytes moved so far for an in-flight phone transfer. Absent until the phone reports progress.
    - `capture_error` string — Reason the capture failed, when it did. Present only for captures.
    - `capture_state` 'detected' | 'uploading' | 'ready' | 'skipped_size' | 'skipped_quota' | 'skipped_type' | 'dropped_teardown' | 'failed' — Capture lifecycle: detected/uploading while in flight, ready when usable, or a terminal skip/failure with its reason. Present only for captures.
    - `checksum` string — SHA-256 of the bytes, computed on the phone during a capture upload. Present only for captures.
    - `created_at` string, date-time, required — When the file was registered: upload registration, or capture detection.
    - `download_url` string — Short-lived signed URL to read the file's bytes. Present only for ready files; re-list to refresh an expired one.
    - `duration_seconds` integer — Video duration in seconds.
    - `filename` string, required — Original filename; used as the display name when the file lands on a phone.
    - `height` integer — Intrinsic pixel height of the source.
    - `id` string, required — File identifier. Unique across the whole library; deliverable to a phone regardless of source.
    - `mime_type` string, required — Declared MIME type, pinned by the presigned upload.
    - `on_phone_count` integer, required — Distinct phones currently holding or receiving a copy. Deleting the file recalls these.
    - `preview_state` 'processing' | 'ready' | 'unavailable', required — Whether the preview exists, is still being generated, or will never be available for this format.
    - `reel_url` string — Short-lived signed URL for the animated hover preview. Videos only; absent until generated.
    - `session_id` string — Session that produced the file. Present only for captures.
    - `size_bytes` integer, required — Declared size in bytes, pinned by the presigned upload.
    - `source` 'upload' | 'capture', required — How the file entered the library: upload (put in directly) or capture (lifted off a session).
    - `status` 'uploading' | 'ready', required — uploading until the object is verified in storage, then ready. For a capture, read capture_state for the fuller lifecycle.
    - `surface` 'phone' — Which surface a capture came off (phone today). Absent for a direct upload.
    - `thumbnail_url` string — Short-lived signed URL for the generated preview image. Absent while generation is pending, and permanently absent for formats without a preview.
    - `width` integer — Intrinsic pixel width of the source.
  - `upload_expires_in_seconds` integer, required — How long the upload URL stays valid.
  - `upload_url` string, required — Presigned S3 PUT URL. PUT the raw file bytes to it with the declared Content-Type and Content-Length headers.

## Other responses

- `default` — Error

## Changes

> 18 revisions in range; 5 could not be searched.

- **2026-07-26** `a0204e0956bc` — 1 breaking
  - api path removed without deprecation
- **2026-07-23** `5a989777701c` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/axilioai/apis/axilio-api/changes/files/post.md)

---

[API](https://skmtc.dev/axilioai/apis/axilio-api.md) · [All operations](https://skmtc.dev/axilioai/apis/axilio-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/axilioai/axilio-api/revisions/8a66f81813b3/schema)
