---
title: "Product kits"
description: "One verified reference sheet that every generation can use."
url: https://docs.roasos.com/concepts/product-kits
---

# Product kits

A **product** in roasOS is more than a folder of photos. When you make a product, roasOS reads the photos and builds the **reference sheet**: one image that shows the product from every angle, with a large close-up of the label. The sheet is the kit. It is the reason a bottle keeps its exact label and its exact shape across an image, a scene and a video.

## What a product carries

| Part            | What it is                                                                                                                                      |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Facts           | the structured description: the category, the form factor, the colours, the materials, the label text, the size class and how a person holds it |
| Reference sheet | ONE generated image: up to nine labeled views of the product on a grid, at 4K                                                                   |
| Raw photos      | the photos that you uploaded; they stay available                                                                                               |

roasOS builds the sheet from the photos and the facts. It then reads the sheet back, and it checks that every label line is legible. A sheet that fails the check does not publish; roasOS retries once with the failure named.

The views on the sheet follow the product's category. A supplement sheet leads with the label close-up and shows the loose gummies. An apparel sheet leads with the front of the garment and shows the fabric and the print. A device sheet leads with the three-quarter view and shows the ports.

A product takes 5 raw photos at most.

## App products

A mobile app is a product too, and it has no object to photograph. Make it with `kind: "app"`. Its images are **real screenshots** taken on the phone, up to 8. Do not use App Store marketing images: those carry device frames, captions and crops, and an ad built on them shows a picture of a picture. roasOS builds no sheet and no kit for an app: a screenshot has no angles, and its pixels are the proof.

When an ad shows the app, it shows one of these screenshots on a phone in the scene. The screenshot goes into the render as a reference image, and the render prompt tells the model to show it exactly as given. The brief picks the screenshot that proves each variant's hook, so upload the screens that carry your claims.

Upload the screenshots with `POST /v1/uploads` and send their URLs in `images`, or send public https URLs of the screenshots in `import_urls`.

Add `app_store_url` for information. roasOS reads the public listing and fills the seller, the category, the price, the rating, the description and the icon. It never takes screenshots from the listing. Any product, of either kind, can carry `product_url`: its own public page, stored for information and never fetched.

```bash
curl -X POST https://api.roasos.com/v1/products \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Capper", "kind": "app", "images": ["https://cdn.roasos.com/uploads/<ws>/images/<id>.png"], "app_store_url": "https://apps.apple.com/us/app/capper-ai-sports-betting/id6758966274" }'
```

`POST /v1/products/{id}/kit` on an app product answers `409 product_kind_no_kit`.

## The legacy angle set

Before the sheet, a kit was a set of separate angle images. That set still exists for callers that ask for it explicitly.

| Set                       | Angles                                                                                     |
| ------------------------- | ------------------------------------------------------------------------------------------ |
| Full, with `"full": true` | `front`, `three_quarter`, `detail`, `in_hand`, `side_left`, `side_right`, `top`, `surface` |
| Named, with `angles`      | any of the nine names, in a list of 1 to 9                                                 |

The ninth name is `back`. roasOS makes a back view from a raw back photo only, and it invents none.

## Status

`GET /v1/products/{id}` gives the kit and its status.

| Status       | Meaning                                                |
| ------------ | ------------------------------------------------------ |
| `none`       | there is no kit; only the raw photos are available     |
| `queued`     | the kit job is waiting                                 |
| `processing` | the kit job is running                                 |
| `ready`      | every angle is complete                                |
| `partial`    | some angles are complete                               |
| `failed`     | the kit job failed; the raw photos are still available |

You do not have to wait. A generation works while the status is `queued` or `processing`. roasOS then uses the raw photos.

## How a generation uses the sheet

Send `product_id` on a model endpoint, and roasOS passes the reference sheet. One reference carries every angle, so the model fuses one identity instead of an average of many photos, and the label arrives large enough to copy.

If you pass product images yourself, pass `reference_sheet` from `GET /v1/products/{id}` as the one product reference. Until a sheet exists, roasOS falls back to the raw photos.

You never select shots yourself. Send `product_id` and let roasOS resolve it. Refer to [Reference media and uploads](/concepts/reference-media).

## Build a kit over the API

The API reference publishes every product operation.

| Operation                    | Path                                                                    | Credits  |
| ---------------------------- | ----------------------------------------------------------------------- | -------- |
| Make a product               | [`POST /v1/products`](/api-reference/products/createProduct)            | 0        |
| List the products            | [`GET /v1/products`](/api-reference/products/listProducts)              | 0        |
| Read one product and its kit | [`GET /v1/products/{id}`](/api-reference/products/getProduct)           | 0        |
| Change a product             | [`PATCH /v1/products/{id}`](/api-reference/products/updateProduct)      | 0        |
| Delete a product             | [`DELETE /v1/products/{id}`](/api-reference/products/deleteProduct)     | 0        |
| Build or rebuild the kit     | [`POST /v1/products/{id}/kit`](/api-reference/products/buildProductKit) | **paid** |

`POST /v1/products` takes a `name` and 1 to 5 photos (1 to 8 screenshots for `kind: "app"`). Put workspace-owned `cdn.roasos.com` URLs in `images`. Put public https URLs in `import_urls`, and roasOS fetches each one for you. For an app, refer to [App products](#app-products).

`POST /v1/products/{id}/kit` needs the scope `api:write` and an `Idempotency-Key` header. The body is optional, and it selects the artifact:

* **An empty body requests the reference sheet.** The answer is `202` with `{"status": "queued", "kind": "reference_sheet"}`. roasOS generates the sheet within minutes, verifies the label, and publishes it as `reference_sheet` on the product. The generation is billed to the workspace like one image.
* `{"full": true}` or an `angles` list runs the **legacy angle job** — paid, asynchronous, with the reservation shape below:

```json
{
  "data": {
    "id": "6f1d…",
    "kind": "product_kit",
    "status": "queued",
    "credits_reserved": 36,
    "included": true,
    "kit_version": 1
  }
}
```

roasOS also builds the sheet by itself when you make a product with photos, so most callers never post to `/kit` at all. Poll `GET /v1/products/{id}` until `reference_sheet` is set.

`credits_reserved` is the worst case, and roasOS refunds what the kit does not use. It holds four parts:

* one cut-out for each photo;
* one upscale for each photo;
* one render for each generated angle;
* one retry for each generated angle.

The retries are the fidelity check. It may make each angle a second time. A `back` view costs no angle render, because roasOS takes it from a raw photo.

`included` tells you what the job costs in the end. roasOS includes the first build of each product. roasOS also includes the first **5** kits of a workspace. It still reserves credits for an included job, because the dispatch needs a reservation. It then settles that job to 0, and it refunds the whole reservation.

A rebuild while a kit job runs answers `409 kit_job_running`. A product with no photos answers `409 product_kit_requires_photos`.

Then poll `GET /v1/products/{id}` until the kit status is `ready`. The kit job publishes onto the product; it does not publish a job result of its own.

### Quote the kit first

You can quote a kit build before you pay for it. Send `"quote_only": true` with the body. The answer is `200` with a quote. The quote charges nothing, and it is valid for 10 minutes:

```json
{
  "data": {
    "kind": "quote",
    "quote_id": "0b7e…",
    "reserved_credits": 36,
    "expires_at": "2026-10-18T12:10:00Z",
    "pricing_version": "customer-pricing@…",
    "authorization": { "status": "auto_approved" },
    "confirmation": {
      "generation_type": "product_kit",
      "product_id": "6f1d…",
      "kit_version": 2,
      "angles": ["front", "three_quarter", "detail", "in_hand"],
      "included": true
    }
  }
}
```

Then send the same body with `"quote_id"` in place of `quote_only`, and the same `Idempotency-Key`. The confirm charges exactly `reserved_credits` and answers the `202` job above. These rules apply:

* With `quote_only` or `quote_id` and neither `full` nor `angles`, the call builds the four core angles. It does not request the reference sheet.
* roasOS refuses a confirm with a different body, a different price or a different kit version. Quote again.
* A confirm that you send again returns the first answer. roasOS does not charge it twice.
* When a kit job already runs for the product, the confirm answers `409 kit_job_running` and charges nothing.
* A quote that expired answers `410`. Quote again.
* `quote_only` together with `quote_id` answers `400`.

A body with no quote field still builds the kit in one call, as before.

An agent makes and reads a product with `roasos_create_product`, `roasos_list_products` and `roasos_get_product`. It builds a kit with `roasos_build_product_kit`, which is quote-first. The change and the delete have no tool, so an agent calls those over REST.

## Files

Each kit file is an immutable object under `https://cdn.roasos.com/workspaces/<workspace>/products/<product>/v<n>/`. A rebuild makes a new version, so an older URL keeps its bytes.

## Next

* Where the photos come from: [Reference media and uploads](/concepts/reference-media).
* What a kit makes possible: [Statics](/concepts/statics).
* One product, one set of ads: [Make static ads](/workflows/make-static-ads).
