Flow · 5 steps
Attach a photo to a SKU
Files — photos, spec sheets, drawings — hang off SKUs and products. The bytes go to a private bucket and the journal records the attachment as a fact; every read answers a signed URL that expires, so a browser can show the picture without the bucket ever being public. The upload is the raw file as the request body, capped at 100 MiB, and carries its own idempotency key: a retry is the same file, not a second one.
Services involved
A filled dot is a read; a ring is a call that changes something.
- Inventory Manager:
https://inventory.getmanifold.io
Every call carries Authorization: Bearer $MANIFOLD_TOKEN unless its step says otherwise
(getting a token). Run a flow in your sandbox, not your live
organization: see Before you start.
1 Create the item
A file needs an owner; an unknown SKU is refused with 404 before a byte is stored.
POST /items Inventory Manager
{
"sku": "WIDGET-PHOTO",
"name": "Widget, photographed",
"quantity": 0
}Answers 201. What the answer holds: the operation's example.
Records inventory.item-created.
2 Upload the photo
The raw bytes as the body, the media type in Content-Type, the filename and the upload's reference in the query string. The answer is the asset with a signed URL good for fifteen minutes.
POST /items/WIDGET-PHOTO/assets?filename=front.png&reference=upload%3AWIDGET-PHOTO%3Afront Inventory Manager
PNG
(not really a png){
"id": "ast_01a15861-f150-7e8f-9a0b-1c2d3e4f5a6b",
"owner": {
"_tag": "sku",
"sku": "WIDGET-PHOTO"
},
"filename": "front.png",
"contentType": "image/png",
"size": 27,
"uploadedAt": "2026-08-17T09:00:00.000Z",
"url": "https://files.example/org_…/ast_3c1f9a2e-…?X-Amz-Expires=900&X-Amz-Signature=…",
"urlExpiresAt": "2026-08-17T09:00:00.000Z"
}From your answer, later steps send id (here ast_01a15861-f150-7e8f-9a0b-1c2d3e4f5a6b) as {assetId}.
Records inventory.asset-uploaded.
3 Retry it
The same reference again — a client that never saw the first answer. The service acknowledges it from the journal: same asset id, nothing read, nothing stored, nothing appended.
POST /items/WIDGET-PHOTO/assets?filename=front.png&reference=upload%3AWIDGET-PHOTO%3Afront Inventory Manager
PNG
(not really a png)Answers 201. What the answer holds: the operation's example.
Records nothing.
4 List the SKU's files
Newest first, each with a fresh signed URL — the one to put in an <img src>.
GET /items/WIDGET-PHOTO/assets Inventory Manager
What the answer holds: the operation's example.
5 Remove it
The fact commits first; the object is deleted after. A retry meets 404.
DELETE /assets/ast_01a15861-f150-7e8f-9a0b-1c2d3e4f5a6b Inventory Manager
{
"id": "ast_01a15861-f150-7e8f-9a0b-1c2d3e4f5a6b",
"owner": {
"_tag": "sku",
"sku": "WIDGET-PHOTO"
},
"filename": "front.png",
"contentType": "image/png",
"size": 27,
"uploadedAt": "2026-08-17T09:00:00.000Z"
}Records inventory.asset-removed.
What a recorded fact is: Facts and the journal. Why a repeated step changes nothing: Idempotency and retries.