# Partajare și organizații (/ro/docs/sessions/sharing-and-orgs)

Acordă un rol pe o sesiune unei persoane, unei organizații sau unui link; listează ce este partajat cu tine; gestionează apartenența la organizații.

Un **share** acordă un `role` pe **o** sesiune unui `user`, fiecărui membru al unei `org` sau oricui prezintă un token de `link`. Owner-ul sesiunii gestionează share-urile; oricine poate crea o organizație și invita oameni în ea.

| `principal_type` | `principal_id` | Cine primește acces |
| --- | --- | --- |
| `user` | UUID-ul utilizatorului | Acel cont, pe orice dispozitiv. |
| `org` | UUID-ul organizației | Fiecare membru curent al organizației. Doar un membru al organizației poate partaja o sesiune în ea. |
| `link` | — (trebuie omis) | Orice principal autentificat care prezintă token-ul. |

`role` este `viewer` (implicit), `editor` sau `owner`. `expires_at` este opțional și trebuie să fie în viitor; un share expirat este ignorat peste tot. Partajarea cu însuși owner-ul sesiunii este `400` — *The owner already has full access.*

## Partajează o sesiune [#partajează-o-sesiune]

<Tabs items={['Cu o persoană', 'Cu o organizație', 'Prin link']}>
<Tab value="Cu o persoană">

```bash
curl -X POST https://ai.codai.ro/v1/sessions/5b3e…/shares \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d '{ "principal_type": "user", "principal_id": "7d21…-uuid", "role": "editor" }'
```

```json
{
  "id": "e4a9…", "session_id": "5b3e…", "principal_type": "user", "principal_id": "7d21…",
  "role": "editor", "has_token": false, "expires_at": null,
  "created_by_user_id": "…", "created_at": "2026-09-23T10:00:00.000Z"
}
```

Utilizator necunoscut → `404 not_found` (*User not found.*). Un `principal_id` care nu e UUID → `400` (*principal_id must be a user uuid.*).

</Tab>
<Tab value="Cu o organizație">

```bash
curl -X POST https://ai.codai.ro/v1/sessions/5b3e…/shares \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d '{ "principal_type": "org", "principal_id": "0c4f…-uuid", "role": "viewer", "expires_at": "2026-10-01T00:00:00+00:00" }'
```

Trebuie să fii membru al acelei organizații — altfel `403 forbidden` (*You are not a member of this org.*). Organizație necunoscută → `404` (*Org not found.*).

</Tab>
<Tab value="Prin link">

```bash
curl -X POST https://ai.codai.ro/v1/sessions/5b3e…/shares \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d '{ "principal_type": "link", "role": "viewer", "expires_at": "2026-09-30T00:00:00+00:00" }'
```

```json
{
  "id": "b12c…", "session_id": "5b3e…", "principal_type": "link", "principal_id": null,
  "role": "viewer", "has_token": true, "expires_at": "2026-09-30T00:00:00.000Z",
  "created_by_user_id": "…", "created_at": "2026-09-23T10:00:00.000Z",
  "token": "Qm9…base64url…"
}
```

`token`-ul are 32 de bytes aleatori, base64url, returnat **exact o dată** în acest `201`. Serverul păstrează doar SHA-256-ul lui, așa că nu poate fi afișat din nou — salvează-l sau creează alt share. Prezintă-l ca `x-codai-share-token` pe orice request, sau `?share=<token>` pe SSE/WS.

</Tab>
</Tabs>

<Callout type="warn">
Un token de link acordă rolul share-ului **oricărui utilizator codai autentificat** care îl prezintă. Tratează-l ca pe o parolă: setează `expires_at`, ține rolul la `viewer` dacă nu ai nevoie de mai mult și șterge share-ul când ai terminat.
</Callout>

## Gestionează share-urile [#gestionează-share-urile]

| Metodă | Cale | Rol minim | Răspuns |
| --- | --- | --- | --- |
| `GET` | `/v1/sessions/:id/shares` | owner | `{ shares: Share[] }` — nu include niciodată token-uri, doar `has_token`. |
| `POST` | `/v1/sessions/:id/shares` | owner | `201` Share (+ `token` o singură dată pentru `link`). |
| `DELETE` | `/v1/sessions/:id/shares/:shareId` | owner | `{ deleted: true, id }`; necunoscut → `404` (*Share not found.*). |

Ștergerea unui share revocă accesul imediat pentru toți cei pe care îi acoperea — inclusiv pentru oricine este conectat acum printr-un token de link, al cărui următor request este `403 not_a_member`.

## Ce este partajat cu mine [#ce-este-partajat-cu-mine]

```bash
curl "https://ai.codai.ro/v1/sessions/shared-with-me" -H "Authorization: Bearer $CODAI_API_KEY"
```

```json
{ "sessions": [ { "id": "5b3e…", "session_key": "…", "owner_user_id": "…", "title": "Fix the flaky test", "created_at": "…", "last_event_at": "…", "last_seq": 42, "executor_device_id": "1c2f…", "lease_expires_at": "…", "e2e": false, "archived": false, "role": "editor", "share_id": "e4a9…" } ] }
```

Listează sesiunile partajate cu tine direct sau printr-o organizație (până la 200; `?archived=1` include și cele arhivate). Accesul prin link share se exercită per request și **nu apare** în nicio listă — un viewer venit prin link trebuie să păstreze token-ul. `GET /v1/sessions` (cu `x-codai-device` sau `?v=2`) returnează întâi sesiunile tale, apoi cele partajate, fiecare cu `role`-ul tău.

Rolul tău efectiv pe o sesiune este **cel mai înalt** dintre toate share-urile care ți se potrivesc — un link `viewer` plus un share de utilizator `editor` te fac editor.

## Organizații [#organizații]

O organizație este un grup de utilizatori cu nume. Partajarea unei sesiuni cu o organizație o partajează cu toți membrii ei, de acum și de mai târziu.

<Callout type="info">
Pe fir obiectul Org este **camelCase** — `{ id, name, ownerUserId, createdAt }` — spre deosebire de orice alt obiect de pe această suprafață. Face parte din v1 și nu va fi redenumit.
</Callout>

| Metodă | Cale | Cine | Request | Răspuns |
| --- | --- | --- | --- | --- |
| `POST` | `/v1/orgs` | orice utilizator | `{ "name": string 1..200 }` | `201 { id, name, ownerUserId, createdAt, role: "owner" }` |
| `GET` | `/v1/orgs` | orice utilizator | — | `{ orgs: [{ id, name, ownerUserId, createdAt, role }] }` — organizațiile din care faci parte, cu rolul tău |
| `GET` | `/v1/orgs/:id/members` | membru al organizației | — | `{ org_id, members: [{ user_id, role, created_at, email }] }` |
| `POST` | `/v1/orgs/:id/members` | owner sau admin al organizației | `{ "user_id": uuid }` **sau** `{ "email": string }` (exact unul), `role?` implicit `member` | `201 { org_id, user_id, role }` — un upsert, deci re-postarea schimbă rolul |
| `DELETE` | `/v1/orgs/:id/members/:userId` | owner sau admin al organizației | — | `{ deleted: true, org_id, user_id }` |

Rolurile de membru sunt `owner` · `admin` · `member`. Doar **owner-ul** organizației poate acorda `owner` (`403` — *Only the org owner can grant the owner role.*) și owner-ul nu poate fi eliminat (`403` — *The org owner cannot be removed.*). Adăugarea cuiva care nu are cont codai este `404` (*User not found.*); un non-membru care citește lista primește `403`.

```bash
# creezi o organizație și adaugi un coleg după email
ORG=$(curl -s -X POST https://ai.codai.ro/v1/orgs \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Platform team" }' | jq -r .id)

curl -X POST "https://ai.codai.ro/v1/orgs/$ORG/members" \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d '{ "email": "ana@example.com", "role": "admin" }'
```

Fiecare mutație de share sau organizație scrie un rând de audit (`share_add`, `share_remove`, `org:create`, `org:member_add`, `org:member_remove`), așa că owner-ul poate reconstitui oricând cine a avut acces și când.
