---
title: "Create Image Upload"
method: POST
path: "/image-uploads"
tags: ["Image uploads"]
---

# Create Image Upload

`POST /image-uploads`

## Request

- Base URL: `https://api.purplegrain.com/api/v1`
- URL: `https://api.purplegrain.com/api/v1/image-uploads`
- Auth: HTTP bearer

Stage a photo that is on the agent's disk, before attaching it to a task
or record. Send `filename`, `content_type` and `byte_size` (and an
optional `checksum`, standard base64 of the raw MD5 or SHA-256 of the
file). The response `data` object carries `upload_id`, a one-time
`upload_url` on this API host, `method` (`PUT`), `headers`, `expires_at`
(about 30 minutes), `max_bytes` (20 MB) and `curl_example`.

Run `curl_example` from the shell, in the directory that contains the
file. The URL authenticates itself: no bearer token. The PUT returns
`{ upload_id, status: "uploaded", content_type, byte_size }`. HEIC and
HEIF are stored as JPEG, EXIF orientation is applied, the capture time
is kept and GPS is removed.

Then pass `upload_id` as `image_upload_ids` on the create or update.
`addTaskImages` appends those photos to a task. `updateTaskImage` is the
legacy call that replaces the gallery with one image; prefer
`addTaskImages`, or `updateTask` with `image_mode` `replace`.
Never base64-encode a local photo into a tool call. A public http(s)
URL uses `image_urls` instead. `image_base64s` is only for a tiny
generated image under about 100 KB decoded.

One upload can be attached to several records (up to 20) until it
expires, 24 hours after it was staged. This call does not write farm
data and does not need `confirm`. The call that attaches the photo still
does. An `Idempotency-Key` header makes a retry of this call return the
same upload.

## Headers

- `Idempotency-Key` string

## Request body

- ImageUploadCreateModelAttributes: Flat request body (application/json) for POST /image-uploads: the fields directly under `data`. Equivalent nested shape: `ImageUploadCreateModel`.
  - `data` ImageUploadCreateFields, required
    - `filename` string, required: File name, used in `curl_example` (`@IMG_0079.heic` when it is a plain basename).
    - `content_type` string, required: Declared type. Must match the file's magic bytes when the PUT arrives. One of image/jpeg, image/png, image/webp, image/heic, image/heif.
    - `byte_size` integer, required: Exact size of the file in bytes. The PUT body must be this long, and it cannot exceed 20 MB.
    - `checksum` string: Optional integrity check of the original file, standard base64 of the raw MD5 (16 bytes) or SHA-256 (32 bytes) digest. Checked against the bytes before HEIC conversion.

## Response `201`

The staged upload. Run `data.curl_example`, then pass `data.upload_id`.

- ImageUploadResponseAttributes: Flat response (Accept application/json). The fields the agent needs are directly under `data`, including `upload_id`, `upload_url` and `curl_example`.
  - `data` ImageUploadFields
    - `id` string
    - `type` string
    - `filename` string
    - `content_type` string
    - `byte_size` integer
    - `upload_id` string: Opaque signed id. Pass it in `image_upload_ids`. It is not the row's uuid and it is not the PUT token.
    - `upload_url` string: One-time PUT URL on this API host. No bearer token; the credential is in the URL, scoped to this user, this upload and this byte size. Expires in about 30 minutes.
    - `method` 'PUT'
    - `headers` object: Headers the PUT must send. Includes Content-Type.
    - `expires_at` string, date-time
    - `max_bytes` integer: Maximum accepted file size, 20 MB.
    - `curl_example` string: Ready to run from the directory that contains the file. For example, curl -sS -X PUT --data-binary @IMG_0079.heic -H "Content-Type: image/heic" followed by the upload_url.

## Other responses

- `401`: Unauthorized
- `422`: The filename, content type or byte size was rejected.

---

[API](https://skmtc.dev/purplegrain/apis/purple-grain-api.md) · [All operations](https://skmtc.dev/purplegrain/apis/purple-grain-api/llms.txt) · [OpenAPI document](https://skmtc.dev/purplegrain/apis/purple-grain-api/revisions/4bedbc1c784c?raw)
