Review queue
After the model, each photo goes through an automatic check that looks for clearly broken results: background left in, a hole cut into the product, a collapsed score. A photo it flags comes back with review: "red", is not charged, and lands in the review queue. Softer doubts (uncertain edges on fur, a subject touching the frame) are noted in the photo’s quality.flags but do not flag it.
Reading the queue
GET/v1/jobs/{id}/review
Only the photos worth a look: flagged, or failed at the model. Failures first, then the lowest scores. Everything else about the batch is left out on purpose.
{
"jobId": "…", "status": "done",
"counts": { "total": 200, "done": 197, "failed": 1, "refunded": 3, … },
"total": 3, "review": 2, "failed": 1,
"items": [
{ "id": "…", "name": "IMG_0412.jpg", "kind": "failed",
"reason": "This photo could not be read", "code": "INVALID_IMAGE",
"severity": null, "score": null, "bbox": null, "flags": [],
"refunded": true, "credits": 1, "canReport": false, "canRerun": true, "attempts": 1,
"inputThumbUrl": "/v1/jobs/…/items/…/file?variant=input-thumb" },
{ "id": "…", "name": "IMG_0418.jpg", "kind": "review",
"reason": "Background left in", "code": "background-kept",
"severity": 0.91, "score": 31, "bbox": { "x": 12, "y": 840, "width": 310, "height": 220 },
"flags": [ { "code": "background-kept", "label": "Background left in", "severity": 0.91, "bbox": { … } } ],
"refunded": true, "credits": 1, "canReport": false, "canRerun": true, "attempts": 1,
"thumbUrl": "/v1/jobs/…/items/…/file?variant=thumb&v=1", "outputUrl": "…", "cutoutUrl": "…" }
]
}reasonis one line a person (or an agent) can act on;codeis stable.bboxis where, in cutout pixels, when the check found a region.thumbUrlis a small WebP of the result, for a quick look without downloading the full file. It is the one thing an agent with vision should fetch.
What to do with a flagged photo
| Option | Call | Cost |
|---|---|---|
| Use it anyway | Nothing: the file is there, and it was not charged. | 0 |
| Run it again | POST /v1/jobs/{id}/items/{itemId}/retry | 1 |
| Run it with our strongest model | the same call with { "tier": "heavy" } | 2 |
| Change the framing or background | /apply (it is a composition problem, not a cutout problem) | 0 |
| Drop it | DELETE /v1/jobs/{id}/items/{itemId} | 0 |
Retrying several at once: POST /v1/jobs/{id}/retry with { "itemIds": ["…"] } or { "itemIds": "failed" }, one reservation for all of them. A retry is a fresh photo: it can be flagged and refunded again.
When the check missed something
POST/v1/jobs/{id}/items/{itemId}/report
A photo looks wrong to you but was charged: tell us where. The server checks the marked region against the pixels, without a model call, and refunds the credit only when the evidence agrees. Two reports per photo at most.
curl -X POST https://trycutstack.com/v1/jobs/$JOB/items/$ITEM/report \
-H "Authorization: Bearer $CUTSTACK_API_KEY" -H "Content-Type: application/json" \
-d '{ "reason": "background-kept", "region": { "x": 10, "y": 820, "width": 320, "height": 240 } }'Reasons: background-kept, missing-part, hole, extra-object, rough-edges. The answer is { job, verdict }; verdict.accepted says whether the credit came back, verdict.evidence says why in one sentence with numbers. A photo that was already refunded answers 409 with reason: "ALREADY_REFUNDED".
The check is deliberately conservative: it flags what is clearly broken and leaves the rest to you. In a typical product catalogue, a few photos in a hundred land in the queue. The event item.needs_review tells you the moment one does (see Events).
Next: Single image. One photo, one call.