---
title: "Reference media and uploads"
description: "How to attach a product, an avatar, a first frame or a voice to a request."
url: https://docs.roasos.com/concepts/reference-media
---

# Reference media and uploads

Each model endpoint takes media in one of two forms. Both forms give roasOS an immutable object on `https://cdn.roasos.com` that your workspace owns. roasOS resolves the object **before** the quote, so the price and the provider payload are pinned to exact bytes.

<Callout type="warn">
  Each media URL must be a workspace-owned object on `https://cdn.roasos.com`. Any other URL gets `400 reference_media_not_workspace_owned`. Your own upload whose bytes have not landed yet gets `409 upload_not_ready` instead: send the bytes, then poll `GET /v1/uploads/{id}` until `status` is `ready`. roasOS does not download from a third-party URL at generation time.
</Callout>

A Discover creative is a competitor's ad. roasOS accepts one in the reference roles and the edit roles; you are responsible for the rights to it. Refer to [Media rights](/concepts/media-rights).

## Form A — a resource id

The workspace already holds the object. roasOS resolves the id to its CDN URLs and applies the role for you.

| Field        | Resource      | Resolves to                                                                                                                   |
| ------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `avatar_id`  | an avatar     | the [character reference sheet](/concepts/character-reference-sheets) or individual views supported by the model, as identity |
| `product_id` | a product     | the product images of the [product kit](/concepts/product-kits)                                                               |
| `scene_id`   | a scene       | the scene image, as the first frame                                                                                           |
| `voice_id`   | a saved voice | the provider voice of your clone                                                                                              |

Read the resources with `GET /v1/avatars`, `GET /v1/scenes`, `GET /v1/products` and `GET /v1/voices`. Each read costs 0 credits.

A face or a voice of a real person needs a consent record on some lanes. Refer to [Media rights](/concepts/media-rights).

## Form B — a CDN URL

Any `https://cdn.roasos.com` object that your workspace owns:

* the result of an earlier job, from `GET /v1/jobs/{id}/result`;
* a file that you uploaded;
* an object of your gallery.

roasOS makes sure that your workspace owns the object and that the object is immutable.

## Roles are fields, not positions

You never count positions in a list. You name the role, and roasOS orders the images for the provider and writes the placement text.

| Role field       | Meaning                                                          |
| ---------------- | ---------------------------------------------------------------- |
| `references`     | style and appearance references                                  |
| `product_images` | product reference images                                         |
| `elements`       | identity: one frontal image and more views, per person or object |
| `image_url`      | the first frame, or the single source image                      |
| `end_image_url`  | the last frame                                                   |
| `video_urls`     | reference videos                                                 |
| `audio_urls`     | reference or driving audio                                       |

One role has one name in every family. Each [model page](/models/image/nano-banana-pro) has a **Media** section that gives the roles, the item counts, the file sizes and the content types of that model. A role that the model does not accept gets `400 unsupported_option`.

## Put your own file on the CDN

`POST /v1/uploads` has two modes.

**Direct.** Send the kind, the content type and the byte length. roasOS gives you a one-use write target.

```json
{ "kind": "image", "content_type": "image/png", "byte_length": 812004 }
```

The response has `id`, `status`, `put_url`, `put_headers` and `limits`. POST the exact bytes to `put_url` with `put_headers`. The engine also accepts `PUT` on that URL. Then poll `GET /v1/uploads/{id}` until `status` is `ready`. The response then has the CDN URL in `url`.

`kind` is `image`, `video` or `audio`. `limits` gives the `max_bytes` and the `allowed_content_types` of that kind. One object is 100 MB at most.

**Import.** Send a public https `source_url` in place of the bytes. roasOS fetches the file, checks it and stores it. The response is the same. Use this mode when the file is on your own site.

```json
{ "kind": "image", "source_url": "https://example.com/bottle.png" }
```

<Callout>
  The API reference publishes both upload operations: [`POST /v1/uploads`](/api-reference/uploads/createUpload) and [`GET /v1/uploads/{id}`](/api-reference/uploads/getUpload). Each one costs 0 credits. An agent calls the same two operations with the tools `roasos_create_upload` and `roasos_get_upload`.
</Callout>

## Prompts stay clean

Do not describe the placement of a reference in your prompt. roasOS adds the placement text for you: it tells the model which image is the person, which is the product, and which is style only. Send a raw prompt.

## Next

* Which role an image does: [Identity and first frames](/concepts/identity-and-first-frames).
* The shots behind `product_id`: [Product kits](/concepts/product-kits).
* Whose media you may send: [Media rights](/concepts/media-rights).
* Cut and join the results: [Assembly](/concepts/assembly).
