---
title: "Attach the packing photo"
method: POST
path: "/orders/{id}/proof"
tags: ["orders"]
---

# Attach the packing photo

`POST /orders/{id}/proof`

## Request

- Base URL: `https://0.0.0.0:3000/api/v1`
- URL: `https://0.0.0.0:3000/api/v1/orders/{id}/proof`
- Auth: API key in header `x-api-key`

The photo taken at packing is what closes production. Attaching it marks every order in the parcel FULFILLED, stamps `fulfilled_at`, and sends each seller's `order_status` webhook — the same act, through the same code, as the camera button on the fulfillment station. This is the call a warehouse's packing step makes when it works OpCreative orders from its own system.

The body is `multipart/form-data`, not JSON — like `POST /products/{id}/images`, it takes a file. `file` must be a PNG, JPG or WEBP of 50 MB or less; the 2 MB JSON limit does not apply here.

The order id in the path identifies the whole parcel: every order sharing its tracking number receives the photo and moves together. Orders already FULFILLED or SHIPPED keep their status — the photo is replaced, the parcel does not move backwards.

A parcel that already has a photo, or is already fulfilled, answers 409 with `requires_overwrite: true` and changes nothing. Re-send with `overwrite=true` to replace it. A second photo is either a correction or a mis-scan, and the API cannot tell which, so it asks.

The key's owner needs the WAREHOUSE role; a seller key gets 403. The photo lands in `proof_image_url`. The design in `image_url` is a different field and is not touched.

## Path parameters

- `id` integer, required

## Headers

- `Idempotency-Key` string

## Response `200`

The order in full, with `proof_image_url` set. `meta.fulfilled` lists every order id in the parcel the photo now covers.

## Other responses

- `401`: No key, or a key that is unknown, revoked or expired. One message for all four — telling you which would confirm to a guesser that a key exists.
- `403`: The key is valid but its owner may not do this.
- `404`: No such record — including records that exist but belong to someone else. The API never confirms another seller's ids.
- `409`: The request contradicts the current state of the record.
- `413`: The request body is over the limit: 2 MB for a JSON body, 50 MB for a proof photo.
- `422`: The body or query is malformed. Field-level detail arrives in `error.fields`.
- `429`: Over the per-key limit. `Retry-After` says how many seconds to wait.
- `500`: Our fault. `error.request_id` matches the X-Request-Id header — quote it to support.

---

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