# Share, review and remix (/docs/gateway/sharing)

Public read-only links to a creative, review links with anchored comments and approval, and remix of a shared creative into your own account.

A creative can be shared through a **link**. A **view link** opens a public, read-only page with the preview; when you allow it,
anyone with a codai API key can **remix** it into their own account. A **review link** opens a page where people without an
account leave comments pinned to a node and a moment of the video, and — when you allow it — approve the creative or
request changes.

Everything on this page is **free**: no model calls, no renders.

| Method | Path | Result |
| --- | --- | --- |
| `POST` | `/v1/studio/creatives/{id}/shares` | Create a view or review link (`201`, token shown once) |
| `GET` | `/v1/studio/creatives/{id}/shares` | Active links of a creative, newest first |
| `DELETE` | `/v1/studio/creatives/{id}/shares/{shareId}` | Revoke a link (`204`) |
| `GET` | `/v1/studio/creatives/{id}/review` | Review status, approved version and thread counts |
| `POST` | `/v1/studio/creatives/{id}/review` | `{ action: "request" }` or `{ action: "reset" }` |
| `GET` | `/v1/studio/creatives/{id}/review/threads?status=` | Comment threads (`open`, `resolved` or `all`) |
| `POST` | `/v1/studio/creatives/{id}/review/threads` | New owner thread (`201`) |
| `POST` | `/v1/studio/creatives/{id}/review/threads/{threadId}/replies` | Owner reply (`201`) |
| `POST` | `/v1/studio/creatives/{id}/review/threads/{threadId}/resolve` | Mark a thread resolved |
| `POST` | `/v1/studio/creatives/{id}/review/threads/{threadId}/reopen` | Reopen a thread |
| `DELETE` | `/v1/studio/creatives/{id}/review/comments/{commentId}` | Delete a comment (`204`) |
| `POST` | `/v1/studio/shares/{token}/remix` | Copy a shared creative into your account (`201`) |
| `GET` | `/v1/public/studio/shares/{token}?format=&locale=` | Public page data (no authentication) |
| `POST` | `/v1/public/studio/reviews/{token}/threads` | Reviewer comment (no authentication, `201`) |
| `POST` | `/v1/public/studio/reviews/{token}/threads/{threadId}/replies` | Reviewer reply (`201`) |
| `POST` | `/v1/public/studio/reviews/{token}/threads/{threadId}/resolve` | Reviewer resolves a thread |
| `POST` | `/v1/public/studio/reviews/{token}/threads/{threadId}/reopen` | Reviewer reopens a thread |
| `POST` | `/v1/public/studio/reviews/{token}/decision` | Approve or request changes (approver links only) |

## View and review links [#view-and-review-links]

```bash
curl https://ai.codai.ro/v1/studio/creatives/<id>/shares \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "review", "label": "Client — Ana", "can_approve": true, "expires_in_days": 14 }'
```

| Field | Meaning |
| --- | --- |
| `kind` | `view` (public read-only page, `/s/<token>`) or `review` (comments and decisions, `/r/<token>`) |
| `label` | Your own note, up to 100 characters (for example who received the link) |
| `expires_in_days` | 1 to 365, or `"never"`; without it the link expires after 30 days |
| `seq` | View links only: pin the page to this version (`400 unknown_seq` when it does not exist); default follows the head |
| `allow_remix` | View links only: whether visitors may remix (default `false`; remix is opt-in) |
| `can_approve` | Review links only: whether this link may approve or request changes (default `false`) |

The answer is **201** `{ "id", "object": "studio.share", "kind", "token", "url", "label", "pinned_seq", "allow_remix",
"can_approve", "expires_at", "created_at" }`. The `token` and the full `url` are returned **only once**: codai keeps
just a SHA-256 hash of the 256-bit token, so a lost link cannot be shown again — revoke it and create another one.
The listing returns the same fields with `url: null`, without `token` and with `last_used_at`.

`DELETE …/shares/{shareId}` revokes a link at once (`404` when it is unknown or already revoked). An expired, revoked
link, or one whose creative was deleted, answers `404 Share link not found or expired` everywhere — the same answer as
a token that never existed.

## The public page [#the-public-page]

`https://hub.codai.ro/s/<token>` shows the title, the preview (video and documents play in a sandboxed frame), a format
and language switcher and, when allowed, **Remix in codai**. A view link shows its pinned version or the head; a review
link always shows the head. The same data comes from `GET /v1/public/studio/shares/{token}`: `kind`, `title`,
`creative_kind`, `seq`, `doc_hash`, `allow_remix`, `can_approve`, `formats`, `default_format`, `locales`, `compiled`
(`{ format, width, height, duration_ms, html }`, or `null` for images) and, for review links, `review` and `threads`.
Asset URLs inside the preview are signed for 1 hour. Public answers are never cached or indexed (`cache-control:
private, no-store`, `x-robots-tag: noindex, nofollow`, `referrer-policy: no-referrer`).

## Remix [#remix]

`POST /v1/studio/shares/{token}/remix` (any codai API key, optional `{ "title" }`) copies the shared version into **your**
account and answers **201** with the new creative plus `remixed_from: { share_id, seq }`:

- the owner's ready assets used by the document are copied into your Studio library (at most 200 assets and 1 GiB,
  otherwise `413 remix_too_large`); missing or unfinished assets stay as references and are listed in `warnings`;
- the brand kit link is dropped (the kit belongs to someone else) — the colours and fonts already in the document stay;
- when you own the shared creative, remix is a normal [fork](./creatives.mdx#versions).

Only view links with `allow_remix` can be remixed; any other link answers `404`. Remixing is free and limited to 20 per
minute per key.

## Review [#review]

A review has a **status**: `draft` → `in_review` → `approved` or `changes_requested`.

- `POST …/review` with `{ "action": "request" }` opens the review for the current head (`requested_seq`) and clears an
  earlier decision; `{ "action": "reset" }` goes back to `draft`.
- Only a reviewer on a link created with `can_approve: true` can decide, with `POST /v1/public/studio/reviews/{token}/decision`
  `{ "name", "decision": "approve" | "request_changes", "seq", "note"? }`. Errors: `403 not_an_approver`,
  `409 review_not_open` (the status is not `in_review`) and `409 stale` with `head_seq` when `seq` is not the current head
  — the reviewer reloads and decides on what you see now.
- An approval **pins the version**: `approved_seq` and `approved_doc_hash`. `GET …/review` returns `approved_is_head`, so
  you can tell when the creative changed after it was approved. Each decision also appears in the threads as a resolved
  comment of kind `decision`, with the note.

`GET …/review` answers `{ "object": "studio.review", "creative_id", "status", "head_seq", "requested_seq", "requested_at",
"approved_seq", "approved_doc_hash", "approved_is_head", "decided_by", "decided_at", "decision_note", "threads": { "open",
"total" } }`.

## Comments and anchors [#comments-and-anchors]

A thread is a root comment plus its replies. Each thread may be anchored with `node_id`, `scene_id` and `t_ms` (0 to
3 600 000 ms), checked against the head: `400 unknown_node` or `400 unknown_scene`. When only `node_id` is given the scene
is derived from it; when only `t_ms` is given on a video, the scene playing at that moment is used. A comment body has
1 to 2000 characters.

A thread is `{ "id", "node_id", "scene_id", "t_ms", "seq", "status": "open" | "resolved", "resolved_by", "resolved_at",
"created_at", "comments" }`; each comment is `{ "id", "kind", "decision", "author": { "kind": "owner" | "reviewer",
"name" }, "body", "created_at" }`, the first one being the root. The owner's name is always `null` (apps show "Owner").
Reviewers send a `name` (1 to 80 characters, otherwise `400 invalid_name`) with every comment, reply, resolve or reopen.
The owner can resolve, reopen and delete any comment; deleting a root hides the whole thread.

## Limits [#limits]

| Limit | Value |
| --- | --- |
| Active links per creative | 20 (`409 too_many_shares`) |
| Threads per creative | 500 (`409 too_many_comments`) |
| Replies per thread | 100 (`409 too_many_comments`) |
| Comment / note | 2000 characters |
| Reviewer name | 80 characters |
| Public reads | 60 per minute per IP, 300 per minute per link |
| Public writes | 10 per minute per IP and link |
| Remix | 20 per minute per key; 200 assets, 1 GiB |

## Privacy [#privacy]

Reviewer names and comments are visible to the owner and to everyone else holding a review link of the same creative.
They are stored with the creative and are **deleted with it** (and with the owner's account). The owner's name is never
shown. Tokens are stored only as hashes; codai never logs tokens, signed URLs, comment bodies or reviewer names.

## SDKs and agent tools [#sdks-and-agent-tools]

```ts
const link = await codai.creatives.createShare(creative.id, { kind: 'review', can_approve: true, expires_in_days: 14 });
console.log(link.url); // shown once
await codai.creatives.requestReview(creative.id);
const threads = await codai.creatives.reviewThreads(creative.id, { status: 'open' });
const copy = await codai.shares.remix('https://hub.codai.ro/s/<token>', { title: 'My version' });
```

```python
link = client.creatives.create_share(creative["id"], kind="review", can_approve=True, expires_in_days=14)
print(link["url"])  # shown once
client.creatives.request_review(creative["id"])
threads = client.creatives.review_threads(creative["id"], status="open")
copy = client.shares.remix("https://hub.codai.ro/s/<token>", title="My version")
```

The SDKs also have `listShares` / `list_shares`, `revokeShare` / `revoke_share`, `review`, `resetReview` /
`reset_review`, the thread methods and, on `shares`, `get`, `comment`, `reply`, `resolve`, `reopen` and `decide` for the
public review flow. They accept a raw token or a full `/s/…` or `/r/…` URL.

Agents on desktop, phone and VS Code use `creative_share` (`creative_id`, `kind`, `can_approve`, `allow_remix`,
`expires_in_days`, `label`), `creative_review` (`action`: `status`, `request` or `reset`; `threads`),
`creative_review_comment` (`action`: `comment`, `reply`, `resolve` or `reopen`; `body`, `thread_id`, `node_id`, `t_ms`)
and `creative_remix` (`share`, `title`).

## Hub (internal) [#hub-internal]

The hub calls the owner routes under `/internal/studio/creatives/:id/shares…` and `/internal/studio/creatives/:id/review…`,
and remix under `/internal/studio/shares/:token/remix`, with a service token and a `user_id`; bodies and errors are
identical. The public pages call the public routes directly.
