codai docs
Gateway

Documente

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 Studio (kind: "document"). Fișierele se deschid și rămân editabile în PowerPoint, Word, Excel, LibreOffice, Google Slides/Docs/Sheets și Keynote.

MetodăCaleRezultat
POST/v1/studio/documents200 — 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}/previews202 — 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}/video202 — transformă o prezentare într-o randare MP4

Modele și prețuri

ModelCe facePreț
codai-docsConstruirea fișierului PPTX / DOCX / XLSXgratuit (cost_micro_usd: "0")
codai-docs-previewPrevizualizare LibreOffice: PDF etichetat, câte un PNG pe pagină, foaie de contact0,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 (vezi Prezentare → MP4).

Cererea

{
  "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âmpImplicitSemnificaț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.
modelcodai-docsSingurul model de documente.
themecodai-lightcodai-light, codai-dark sau corporate (vezi mai jos).
titleprimul titlu de slide / secțiunePână la 200 de caractere; scris în proprietățile fișierului.
languagero-ROLimba 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).
previewfalsePune în coadă și o previzualizare, taxată ca POST …/previews.

Răspunsul:

{
  "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

TemăFontAspect
codai-lightManropeculorile brandului codai pe fundal deschis
codai-darkManropeculorile brandului codai pe fundal închis
corporateAptos (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

Schemele complete sunt în referința API (DocumentCreateRequest, DeckSpec, DocSpec, SheetSpec). Câmpurile necunoscute sunt respinse.

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.

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):

layoutCâmpuri
titletitle, subtitle?
sectiontitle, subtitle?
bulletstitle, bullets (1–12)
two_columntitle, left, right — fiecare coloană { heading?, text?, bullets?, image?, chart? }
imagetitle?, image
charttitle, chart
tabletitle, table
quotequote, attribution?
statstitle?, stats (1–4 de { value, label, delta? })
timelinetitle, items (2–8 de { date, label, detail? })
closingtitle, subtitle?, contact?

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 blocCâmpuri
headinglevel (1–4), text
paragraphtext (RichText), align? (left, center, right, justify)
listitems (1–200 de puncte), ordered?
tabletable
imageimage
chartchart
quotetext, attribution?
page_break—

Foi de calcul (type: "sheet")

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

CâmpImplicitSemnificație
name—Unic, 1–31 de caractere, fără []:*?/\.
columns—{ header, width?, format? } pentru fiecare coloană.
rows—Liste de celule.
freeze_headertrueÎngheață rândul de antet.
autofiltertrueButoane de filtrare pe antet.
totalsfalseAdaugă 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:

formatFormat Excel
ron#,##0.00 "lei"
eur#,##0.00 "€"
usd"$"#,##0.00
percent0.0%
datedd.mm.yyyy
datetimedd.mm.yyyy hh:mm
text, integer, numberdupă nume

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:

PPTXDOCXXLSX
bar … scattergrafic nativ, editabilPNG Vega-LitePNG Vega-Lite + datele ca tabel alături
vegaPNG Vega-LitePNG Vega-LitePNG Vega-Lite + datele ca tabel alături

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

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

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

{ "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

În loc să editezi spec-ul JSON, poți transforma un document într-o creație 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

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:

{
  "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

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 (codai-render) care arată câte un slide pe pagină, cu tranziție fade sau fără.

{
  "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âmpImplicitSemnificație
qualityfinalfinal sau draft, ca la randări.
seconds_per_slide52–20 s pe slide când nu există narațiune.
transitionfadefade 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 (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

LimităValoare
Slide-uri per prezentare60
Blocuri per document2.000
Foi per foaie de calcul20
Celule per foaie de calcul200.000
Grafice per document20
Mărimea fișierului generat50 MB
Pagini randate în previzualizare60
Previzualizări active per utilizator3
Durata video-ului din prezentare180 s

Erori

HTTPcodeCând
400invalid_specCorpul nu respectă schema; primele probleme sunt în mesaj, lista completă în details.
400invalid_based_onbased_on nu e unul dintre documentele tale de același type.
400asset_not_foundO imagine (sau asset-ul muzical) e necunoscută, nu e a ta, nu e ready sau nu e imagine (audio, pentru muzică).
400unsupported_imageFormatul imaginii nu poate fi inclus în fișier.
400too_many_chartsPeste 20 de grafice într-un document.
400chart_render_failedUn grafic nu a putut fi randat.
400not_a_deckS-a cerut video pentru un doc sau sheet.
400video_too_longVideo-ul prezentării ar dura peste 180 s.
400bad_requestCorp invalid la previzualizare sau la video.
403forbiddenCheia nu are voie să folosească codai-docs, codai-docs-preview, codai-render sau modelul de narațiune.
403residency_unavailableS-a cerut narațiune pe un cont cu rezidența datelor în UE.
402 / 429subscription_inactive / rate_limit_exceededAbonament inactiv sau plafon de cheltuieli atins.
404not_foundId necunoscut sau documentul ori previzualizarea altui utilizator.
413document_too_largeFișierul generat depășește 50 MB.
429too_many_previewsAi deja 3 previzualizări în coadă sau în lucru.
429too_many_rendersAi deja 3 randări în coadă sau în lucru.
503documents_unavailableDocumentele sau previzualizările nu sunt configurate pe acest gateway.

Exemple

curl

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

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

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"]).

Pe această pagină