# Documente (/ro/docs/gateway/documents)

Documente codai Studio — construiești prezentări (PPTX), documente (DOCX) și foi de calcul (XLSX) dintr-un spec JSON, le previzualizezi ca PDF și PNG și transformi prezentările în MP4-uri narate.

Un **document** este un fișier Office construit dintr-un spec JSON: o prezentare (`.pptx`), un document (`.docx`) sau o foaie de calcul (`.xlsx`). Descrii conținutul, gateway-ul construiește fișierul sincron și îl stochează ca [asset](./assets.mdx) Studio (`kind: "document"`). Fișierele se deschid și rămân editabile în PowerPoint, Word, Excel, LibreOffice, Google Slides/Docs/Sheets și Keynote.

| Metodă | Cale                                 | Rezultat                                                                    |
| ------ | ------------------------------------ | --------------------------------------------------------------------------- |
| `POST` | `/v1/studio/documents`               | `200` — fișierul e construit; întoarce documentul și `file.url` semnat      |
| `GET`  | `/v1/studio/documents/{id}`          | Documentul, un `file.url` proaspăt, `spec`-ul stocat și `latest_preview_id` |
| `POST` | `/v1/studio/documents/{id}/previews` | `202` — pune în coadă o previzualizare (PDF etichetat + PNG-uri pe pagini)  |
| `GET`  | `/v1/studio/previews/{id}`           | Starea previzualizării, plus PDF-ul și paginile după `succeeded`            |
| `POST` | `/v1/studio/documents/{id}/video`    | `202` — transformă o prezentare într-o randare MP4                          |

## Modele și prețuri [#modele-și-prețuri]

| Model                | Ce face                                                                            | Preț                                                            |
| -------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| `codai-docs`         | Construirea fișierului PPTX / DOCX / XLSX                                          | **gratuit** (`cost_micro_usd: "0"`)                             |
| `codai-docs-preview` | Previzualizare LibreOffice: PDF etichetat, câte un PNG pe pagină, foaie de contact | **0,002 $ pe pagină**, minimum **0,01 $**, taxat o singură dată |

O previzualizare se taxează la primul poll care vede rezultatul; previzualizările eșuate nu costă nimic. Video-urile din prezentări se taxează ca [randări](./renders.mdx) (vezi [Prezentare → MP4](#prezentare--mp4)).

## Cererea [#cererea]

```json
{
  "model": "codai-docs",
  "type": "deck",
  "theme": "codai-light",
  "title": "Rezultate T3",
  "language": "ro-RO",
  "filename": "rezultate-t3",
  "preview": true,
  "spec": {
    "slides": [
      {
        "layout": "title",
        "title": "Rezultate T3",
        "subtitle": "Octombrie 2026",
        "notes": "Bun venit."
      },
      {
        "layout": "chart",
        "title": "Venituri",
        "chart": {
          "chart_type": "column",
          "alt": "Veniturile cresc de la 1,2 la 1,9 milioane lei",
          "categories": ["Iul", "Aug", "Sep"],
          "series": [{ "name": "Venituri", "values": [1.2, 1.5, 1.9] }]
        }
      }
    ]
  }
}
```

| Câmp       | Implicit                         | Semnificație                                                                                                          |
| ---------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `type`     | —                                | `deck` (PPTX), `doc` (DOCX) sau `sheet` (XLSX); obligatoriu. Alege schema lui `spec`.                                 |
| `spec`     | —                                | Conținutul (obligatoriu); trebuie să corespundă cu `type`. Vezi [Referința spec-ului](#referința-spec-ului).          |
| `model`    | `codai-docs`                     | Singurul model de documente.                                                                                          |
| `theme`    | `codai-light`                    | `codai-light`, `codai-dark` sau `corporate` (vezi mai jos).                                                           |
| `title`    | primul titlu de slide / secțiune | Până la 200 de caractere; scris în proprietățile fișierului.                                                          |
| `language` | `ro-RO`                          | Limba de corectură BCP-47 a textului; și limba implicită a narațiunii.                                                |
| `filename` | —                                | Numele de descărcare, fără extensie (până la 120 de caractere, curățat).                                              |
| `based_on` | —                                | Unul dintre documentele tale de același `type`; creează versiunea următoare (vezi [Editarea](#editarea-cu-based_on)). |
| `preview`  | `false`                          | Pune în coadă și o previzualizare, taxată ca `POST …/previews`.                                                       |

Răspunsul:

```json
{
  "id": "5a7b2c3d-0000-4000-8000-000000000020",
  "object": "studio.document",
  "type": "deck",
  "theme": "codai-light",
  "model": "codai-docs",
  "title": "Rezultate T3",
  "version": 1,
  "based_on": null,
  "warnings": [],
  "cost_micro_usd": "0",
  "file": {
    "asset_id": "5a7b2c3d-…",
    "kind": "document",
    "url": "https://storage.googleapis.com/…",
    "mime_type": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
    "…": "…"
  },
  "preview": { "id": "6b8c3d4e-…", "object": "studio.preview", "status": "queued" }
}
```

`file.url` este un URL semnat, valabil o oră; obții oricând unul proaspăt cu `GET /v1/studio/documents/{id}` sau `GET /v1/assets/{id}`. `warnings` enumeră observații nefatale, de exemplu o notă de subsol ignorată într-o prezentare.

### Teme [#teme]

| Temă          | Font                                                         | Aspect                                     |
| ------------- | ------------------------------------------------------------ | ------------------------------------------ |
| `codai-light` | Manrope                                                      | culorile brandului codai pe fundal deschis |
| `codai-dark`  | Manrope                                                      | culorile brandului codai pe fundal închis  |
| `corporate`   | Aptos (Office îl înlocuiește cu Calibri când Aptos lipsește) | stil de afaceri neutru                     |

Fonturile **nu sunt incluse** în fișiere: un calculator fără Manrope afișează un font de rezervă. Diacriticele românești (ș ț ă î â) se păstrează peste tot.

## Referința spec-ului [#referința-spec-ului]

Schemele complete sunt în [referința API](../api/index.mdx) (`DocumentCreateRequest`, `DeckSpec`, `DocSpec`, `SheetSpec`). Câmpurile necunoscute sunt respinse.

### Tipuri comune [#tipuri-comune]

- **RichText** — un șir (până la 10.000 de caractere) sau o listă de 1–200 de **fragmente** `{ text, bold?, italic?, underline?, code?, link?, footnote? }`. `link` trebuie să fie un URL `https`; `footnote` funcționează doar în DOCX (ignorat cu un avertisment în prezentări).
- **Bullet** — RichText sau `{ text, level }`, cu un nivel de indentare 0–3.
- **Image** — `{ asset_id, alt, caption? }`. `asset_id` este unul dintre asset-urile tale imagine `ready` (generate sau încărcate); `alt` este **obligatoriu**.
- **Table** — `{ columns (1–20), rows (≤ 500, celule string | number | null), header? (true), align?, caption? }`. Un rând poate fi mai scurt decât `columns`, niciodată mai lung.
- **Chart** — vezi [Grafice](#grafice).

### Prezentări (`type: "deck"`) [#prezentări-type-deck]

`{ aspect? ("16:9" | "4:3", implicit 16:9), footer?, slide_numbers? (true), slides: Slide[1..60] }`. Fiecare slide alege un `layout` și poate avea `notes` (notele vorbitorului, până la 5.000 de caractere):

| `layout`     | Câmpuri                                                                                    |
| ------------ | ------------------------------------------------------------------------------------------ |
| `title`      | `title`, `subtitle?`                                                                       |
| `section`    | `title`, `subtitle?`                                                                       |
| `bullets`    | `title`, `bullets` (1–12)                                                                  |
| `two_column` | `title`, `left`, `right` — fiecare coloană `{ heading?, text?, bullets?, image?, chart? }` |
| `image`      | `title?`, `image`                                                                          |
| `chart`      | `title`, `chart`                                                                           |
| `table`      | `title`, `table`                                                                           |
| `quote`      | `quote`, `attribution?`                                                                    |
| `stats`      | `title?`, `stats` (1–4 de `{ value, label, delta? }`)                                      |
| `timeline`   | `title`, `items` (2–8 de `{ date, label, detail? }`)                                       |
| `closing`    | `title`, `subtitle?`, `contact?`                                                           |

### Documente (`type: "doc"`) [#documente-type-doc]

`{ page_size? ("A4" | "Letter"), orientation? ("portrait" | "landscape"), header?, footer?, page_numbers? (true), toc? (false), blocks: Block[1..2000] }`. `toc: true` inserează un cuprins construit din titluri.

| `type` de bloc | Câmpuri                                                            |
| -------------- | ------------------------------------------------------------------ |
| `heading`      | `level` (1–4), `text`                                              |
| `paragraph`    | `text` (RichText), `align?` (`left`, `center`, `right`, `justify`) |
| `list`         | `items` (1–200 de puncte), `ordered?`                              |
| `table`        | `table`                                                            |
| `image`        | `image`                                                            |
| `chart`        | `chart`                                                            |
| `quote`        | `text`, `attribution?`                                             |
| `page_break`   | —                                                                  |

### Foi de calcul (`type: "sheet"`) [#foi-de-calcul-type-sheet]

`{ sheets: Sheet[1..20] }`, cel mult 200.000 de celule în total. O foaie:

| Câmp            | Implicit | Semnificație                                                                                                                                                                                    |
| --------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`          | —        | Unic, 1–31 de caractere, fără `[]:*?/\`.                                                                                                                                                        |
| `columns`       | —        | `{ header, width?, format? }` pentru fiecare coloană.                                                                                                                                           |
| `rows`          | —        | Liste de celule.                                                                                                                                                                                |
| `freeze_header` | `true`   | Îngheață rândul de antet.                                                                                                                                                                       |
| `autofilter`    | `true`   | Butoane de filtrare pe antet.                                                                                                                                                                   |
| `totals`        | `false`  | Adaugă un rând SUM pentru coloanele numerice.                                                                                                                                                   |
| `conditional`   | —        | `{ range: "B2:B20", rule, value?, value2?, style? }`; reguli `gt`, `lt`, `eq`, `between` (cer `value`, iar `between` și `value2`), `color_scale`, `data_bar`; stiluri `good`, `bad`, `neutral`. |
| `charts`        | —        | `{ chart, anchor?: "H2" }`.                                                                                                                                                                     |

O **celulă** este un șir, un număr, un boolean, `null`, `{ "formula": "SUM(B2:B10)" }` (fără `=` la început) sau `{ "date": "2026-10-10" }` (dată sau dată-oră ISO). Formatele de coloană urmează convențiile ro-RO:

| `format`                    | Format Excel       |
| --------------------------- | ------------------ |
| `ron`                       | `#,##0.00 "lei"`   |
| `eur`                       | `#,##0.00 "€"`     |
| `usd`                       | `"$"#,##0.00`      |
| `percent`                   | `0.0%`             |
| `date`                      | `dd.mm.yyyy`       |
| `datetime`                  | `dd.mm.yyyy hh:mm` |
| `text`, `integer`, `number` | după nume          |

## Grafice [#grafice]

`{ chart_type, alt, title?, categories?, series?, stacked?, number_format?, vega_lite? }`, cel mult **20 de grafice per document**.

- `chart_type` este `bar`, `column`, `line`, `area`, `pie`, `doughnut`, `scatter` sau `vega`.
- Pentru toate tipurile în afară de `vega`, `categories` și `series` (1–12 de `{ name, values }`) sunt obligatorii, iar fiecare serie are exact câte o valoare pentru fiecare categorie. `number_format` este un format numeric Excel, de exemplu `#,##0 "lei"`.
- `vega` primește un spec `vega_lite` doar cu **date în linie** — orice `url` (încărcări de date, marcaje de imagine) este respins — de cel mult 500 kB.

Cum sunt desenate:

|                   | PPTX                   | DOCX          | XLSX                                    |
| ----------------- | ---------------------- | ------------- | --------------------------------------- |
| `bar` … `scatter` | grafic nativ, editabil | PNG Vega-Lite | PNG Vega-Lite + datele ca tabel alături |
| `vega`            | PNG Vega-Lite          | PNG Vega-Lite | PNG Vega-Lite + datele ca tabel alături |

Un grafic care nu poate fi randat oprește cererea cu `400 chart_render_failed`.

## Accesibilitate [#accesibilitate]

- `alt` este **obligatoriu** la fiecare imagine și grafic; devine textul alternativ în fișier și în PDF.
- PDF-urile de previzualizare sunt **etichetate (tagged)**, iar ordinea de citire urmează ordinea din spec.
- Setează `language` ca cititoarele de ecran și corectorul ortografic să folosească limba potrivită.

## Editarea cu `based_on` [#editarea-cu-based_on]

`GET /v1/studio/documents/{id}` întoarce `spec`-ul stocat. Îl modifici și îl trimiți înapoi cu `based_on` setat la id-ul documentului:

```json
{ "type": "deck", "based_on": "5a7b2c3d-0000-4000-8000-000000000020", "spec": { "slides": [ … ] } }
```

Rezultatul este un document **nou**, cu `version` = precedenta + 1 și `based_on` spre cel editat; fișierul vechi rămâne neschimbat. `based_on` trebuie să fie unul dintre documentele tale de același `type`, altfel `400 invalid_based_on`.

## Editare ca o creație [#editare-ca-o-creație]

În loc să editezi `spec`-ul JSON, poți transforma un document într-o [creație](./creatives.mdx#documente-prezentare-document-foaie-de-calcul)
editabilă: `POST /v1/studio/documents/{id}/creative` îl convertește (gratuit și idempotent — apelurile următoare întorc
aceeași creație) și îl leagă de document, care raportează apoi `creative_id`. O editezi prin operații sau în editorul
Studio, cu previzualizare live, iar `POST /v1/studio/creatives/{id}/build` generează o versiune nouă a acestui document
(`version` + 1).

## Previzualizări [#previzualizări]

`POST /v1/studio/documents/{id}/previews` (sau `preview: true` la creare) convertește fișierul cu LibreOffice în:

- un **PDF etichetat** (`pdf`, un asset cu `kind: "preview"`);
- câte un **PNG pe pagină**, lat de 1280 px, cel mult 60 de pagini (`pages`);
- o **foaie de contact** cu toate paginile într-o singură imagine (`contact_sheet_url`).

Interoghează `GET /v1/studio/previews/{id}` până când `status` este `succeeded` sau `failed`:

```json
{
  "id": "6b8c3d4e-0000-4000-8000-000000000021",
  "object": "studio.preview",
  "document_id": "5a7b2c3d-0000-4000-8000-000000000020",
  "model": "codai-docs-preview",
  "status": "succeeded",
  "error": null,
  "page_count": 2,
  "cost_micro_usd": "10000",
  "pdf": {
    "asset_id": "6b8c3d4e-…",
    "kind": "preview",
    "mime_type": "application/pdf",
    "url": "https://storage.googleapis.com/…",
    "…": "…"
  },
  "pages": [
    { "page": 1, "url": "https://storage.googleapis.com/…p001.png…", "width": 1280, "height": 720 }
  ],
  "contact_sheet_url": "https://storage.googleapis.com/…contact.png…"
}
```

Toate URL-urile sunt valabile o oră. Cel mult **3 previzualizări** pot fi în coadă sau în lucru per utilizator în același timp (`429 too_many_previews`). Previzualizarea este ce ar trebui să verifice un agent ca să controleze aranjarea înainte să trimită fișierul mai departe.

## Prezentare → MP4 [#prezentare--mp4]

`POST /v1/studio/documents/{id}/video` transformă o **prezentare** într-un video: gateway-ul pune în coadă o previzualizare de 1920×1080, apoi o [randare](./renders.mdx) (`codai-render`) care arată câte un slide pe pagină, cu tranziție fade sau fără.

```json
{
  "quality": "final",
  "seconds_per_slide": 5,
  "transition": "fade",
  "music_asset_id": "3f2b8c1e-0000-4000-8000-000000000002",
  "narrate": { "model": "codai-speech", "voice": "Achernar", "language": "ro-RO" }
}
```

| Câmp                | Implicit | Semnificație                                                                                  |
| ------------------- | -------- | --------------------------------------------------------------------------------------------- |
| `quality`           | `final`  | `final` sau `draft`, ca la randări.                                                           |
| `seconds_per_slide` | `5`      | 2–20 s pe slide când nu există narațiune.                                                     |
| `transition`        | `fade`   | `fade` sau `none`.                                                                            |
| `music_asset_id`    | —        | Unul dintre asset-urile tale audio `ready`, mixat sub slide-uri la −18 dB.                    |
| `narrate`           | —        | `{ model? ("codai-speech" \| "codai-speech-expressive"), voice?, language?, instructions? }`. |

Cu `narrate`, sunt citite `notes` ale fiecărui slide (fără note: titlul, subtitlul și textul punctelor), iar un slide durează max(2 s, narațiunea + 0,6 s). Tot video-ul trebuie să încapă în **180 de secunde** (`400 video_too_long`). Narațiunea nu este disponibilă pentru conturile cu rezidența datelor în UE (`403 residency_unavailable`).

Răspunsul `202` este o randare (`object: "studio.render"`) plus `preview_id`, `narration_cost_micro_usd`, `slides` și `duration_seconds`; o interoghezi cu `GET /v1/studio/renders/{id}`. Prețuri:

- **narațiunea** — taxată imediat la prețul [text-to-speech](./embeddings-audio.mdx) (`narration_cost_micro_usd`, și în antetul `x-codai-narration-cost-micro-usd`);
- **previzualizarea** — `codai-docs-preview`, 0,002 $ pe pagină de slide (minimum 0,01 $);
- **randarea** — `codai-render` pe secundă de ieșire (0,01 $ final / 0,005 $ draft), taxată la poll-ul randării.

## Limite [#limite]

| Limită                               | Valoare |
| ------------------------------------ | ------- |
| Slide-uri per prezentare             | 60      |
| Blocuri per document                 | 2.000   |
| Foi per foaie de calcul              | 20      |
| Celule per foaie de calcul           | 200.000 |
| Grafice per document                 | 20      |
| Mărimea fișierului generat           | 50 MB   |
| Pagini randate în previzualizare     | 60      |
| Previzualizări active per utilizator | 3       |
| Durata video-ului din prezentare     | 180 s   |

## Erori [#erori]

| HTTP      | `code`                                          | Când                                                                                                             |
| --------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| 400       | `invalid_spec`                                  | Corpul nu respectă schema; primele probleme sunt în mesaj, lista completă în `details`.                          |
| 400       | `invalid_based_on`                              | `based_on` nu e unul dintre documentele tale de același `type`.                                                  |
| 400       | `asset_not_found`                               | O imagine (sau asset-ul muzical) e necunoscută, nu e a ta, nu e `ready` sau nu e imagine (audio, pentru muzică). |
| 400       | `unsupported_image`                             | Formatul imaginii nu poate fi inclus în fișier.                                                                  |
| 400       | `too_many_charts`                               | Peste 20 de grafice într-un document.                                                                            |
| 400       | `chart_render_failed`                           | Un grafic nu a putut fi randat.                                                                                  |
| 400       | `not_a_deck`                                    | S-a cerut video pentru un `doc` sau `sheet`.                                                                     |
| 400       | `video_too_long`                                | Video-ul prezentării ar dura peste 180 s.                                                                        |
| 400       | `bad_request`                                   | Corp invalid la previzualizare sau la video.                                                                     |
| 403       | `forbidden`                                     | Cheia nu are voie să folosească `codai-docs`, `codai-docs-preview`, `codai-render` sau modelul de narațiune.     |
| 403       | `residency_unavailable`                         | S-a cerut narațiune pe un cont cu rezidența datelor în UE.                                                       |
| 402 / 429 | `subscription_inactive` / `rate_limit_exceeded` | Abonament inactiv sau plafon de cheltuieli atins.                                                                |
| 404       | `not_found`                                     | Id necunoscut sau documentul ori previzualizarea altui utilizator.                                               |
| 413       | `document_too_large`                            | Fișierul generat depășește 50 MB.                                                                                |
| 429       | `too_many_previews`                             | Ai deja 3 previzualizări în coadă sau în lucru.                                                                  |
| 429       | `too_many_renders`                              | Ai deja 3 randări în coadă sau în lucru.                                                                         |
| 503       | `documents_unavailable`                         | Documentele sau previzualizările nu sunt configurate pe acest gateway.                                           |

## Exemple [#exemple]

### curl [#curl]

```bash
DOC=$(curl -s https://ai.codai.ro/v1/studio/documents \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d @deck.json)
curl -L -o rezultate-t3.pptx "$(echo "$DOC" | jq -r .file.url)"

PREVIEW=$(echo "$DOC" | jq -r .preview.id)
while :; do
  P=$(curl -s "https://ai.codai.ro/v1/studio/previews/$PREVIEW" -H "Authorization: Bearer $CODAI_API_KEY")
  S=$(echo "$P" | jq -r .status)
  [ "$S" = succeeded ] || [ "$S" = failed ] && break
  sleep 3
done
curl -L -o rezultate-t3.pdf "$(echo "$P" | jq -r .pdf.url)"

# Prezentare -> MP4 narat, apoi interoghezi /v1/studio/renders/{id}
curl -s "https://ai.codai.ro/v1/studio/documents/$(echo "$DOC" | jq -r .id)/video" \
  -H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"narrate":{"model":"codai-speech","language":"ro-RO"}}'
```

### SDK TypeScript [#sdk-typescript]

```ts
import { Codai } from 'codai-sdk';
const client = new Codai({ apiKey: process.env.CODAI_API_KEY! });

const doc = await client.documents.create({
  type: 'sheet',
  theme: 'corporate',
  filename: 'facturi-octombrie',
  spec: {
    sheets: [
      {
        name: 'Facturi',
        columns: [
          { header: 'Client' },
          { header: 'Data', format: 'date' },
          { header: 'Total', format: 'ron' },
        ],
        rows: [
          ['Alfa SRL', { date: '2026-10-01' }, 1250.5],
          ['Beta SA', { date: '2026-10-03' }, 980],
        ],
        totals: true,
      },
    ],
  },
});
console.log(doc.file.url);

const queued = await client.documents.preview(doc.id);
const preview = await client.documents.retrievePreview(queued.id);
const again = await client.documents.retrieve(doc.id); // include spec-ul stocat
```

Pentru prezentări, `client.documents.video(id, { narrate: { model: 'codai-speech' } })` întoarce randarea pusă în coadă; o interoghezi cu `client.renders.retrieve(render.id)`.

### SDK Python [#sdk-python]

```python
import os
from codai import Codai

client = Codai(api_key=os.environ["CODAI_API_KEY"])
doc = client.documents.create({
    "type": "doc",
    "title": "Raport lunar",
    "spec": {
        "toc": True,
        "blocks": [
            {"type": "heading", "level": 1, "text": "Raport lunar"},
            {"type": "paragraph", "text": [{"text": "Veniturile au crescut cu "}, {"text": "18%", "bold": True}, {"text": "."}]},
            {"type": "table", "table": {"columns": ["Lună", "Venituri"], "rows": [["Septembrie", 1.5], ["Octombrie", 1.9]]}},
        ],
    },
})
print(doc["file"]["url"])

queued = client.documents.preview(doc["id"])
preview = client.documents.retrieve_preview(queued["id"])
stored = client.documents.retrieve(doc["id"])  # include spec-ul stocat
```

`client.documents.video(deck_id, {...})` pune în coadă un video din prezentare; îl interoghezi cu `client.renders.retrieve(render["id"])`.
