codai docs
Gateway

Creații

Documente creative editabile în codai Studio — creezi un video, o imagine sau o prezentare ca JSON, le editezi prin operații, păstrezi fiecare versiune și le randezi în MP4.

O creație este un singur document editabil pentru fiecare produs Studio (video, imagine, prezentare, document sau foaie de calcul), păstrat ca JSON simplu — Editable Creative Document (ECD). Agenții și editoarele nu rescriu niciodată HTML: trimit operații mici, adresate prin id. Fiecare editare adaugă o versiune, deci poți oricând să te uiți înapoi, să revii sau să copiezi. Intrarea pentru randare este o funcție pură și deterministă a documentului.

Crearea, citirea și editarea creațiilor sunt gratuite. Costă doar randările și cadrele statice peste cota zilnică gratuită.

MetodăCaleRezultat
POST/v1/studio/creativesCreație nouă la versiunea 1 (201)
GET/v1/studio/creatives?kind=&before=&limit={ object: "list", data, next_before }, cele modificate recent primele
GET/v1/studio/creatives/{id}?seq=Documentul curent (sau de la seq) + asset-urile semnate
GET/v1/studio/creatives/{id}/outlineSchiță text compactă, formate și variabile
POST/v1/studio/creatives/{id}/findId-urile nodurilor care se potrivesc
POST/v1/studio/creatives/{id}/subtreeJSON-ul complet al unor noduri sau al unei scene
POST/v1/studio/creatives/{id}/opsAplică un lot de operații → versiune nouă
GET/v1/studio/creatives/{id}/versionsIstoricul versiunilor
POST/v1/studio/creatives/{id}/revertVersiune curentă nouă cu documentul unei versiuni vechi
POST/v1/studio/creatives/{id}/forkCreație nouă dintr-o versiune (201)
POST/v1/studio/creatives/{id}/lintContrast, zonă sigură, brand, accesibilitate, asset-uri lipsă…
GET/v1/studio/creatives/{id}/compiled?format=&scene=HTML de previzualizare cu URL-uri semnate
POST/v1/studio/creatives/{id}/renderRandare MP4 (202, codai-render; 200 când randarea identică există deja)
POST/v1/studio/creatives/{id}/frameCadre PNG statice la cel mult 6 momente (200 sau 202 + un job de interogat)
GET/v1/studio/creatives/{id}/frames/{jobId}Interoghează un job de cadre (200 cadre, 202 încă rulează)
POST/v1/studio/creatives/{id}/commentsTransformă un comentariu pe un cadru într-o editare (facturat pe tokeni)
POST/v1/studio/creatives/{id}/variantsGenerează variante A/B ca derivate + randări draft (facturat pe tokeni)
GET/v1/studio/creatives/{id}/variantsUltimul grup de variante, cu stări și starea randării
POST/v1/studio/creatives/{id}/variants/{variantId}/pickAplică varianta câștigătoare pe original
DELETE/v1/studio/creatives/{id}Ștergere
POST/v1/studio/brand-kitsCreează un kit de brand din tokeni sau dintr-un fișier DTCG (201)
POST/v1/studio/brand-kits/extractKit de brand (paletă, fonturi, logo) dintr-o pagină web sau dintr-o imagine de logo (201, facturat pe tokeni)
GET/v1/studio/brand-kits?limit=Kiturile tale de brand, cele mai noi primele
GET/v1/studio/brand-kits/{id}Un kit de brand cu URL semnat al logoului
PATCH/v1/studio/brand-kits/{id}Editează un kit de brand (nume, tokeni, DTCG, logo)
GET/v1/studio/brand-kits/{id}/dtcgDescarcă un kit de brand ca fișier DTCG .tokens.json
DELETE/v1/studio/brand-kits/{id}Șterge un kit de brand
POST/v1/studio/creatives/{id}/restyleReaplică un kit de brand pe o creație → versiune nouă
POST/v1/studio/creatives/{id}/captionsSubtitrări automate dintr-un clip de voce → versiune nouă (transcrierea se facturează pe minut)
GET/v1/studio/creatives/{id}/captions.srt?node_id=&seq=Descarcă subtitrările în format SubRip
GET/v1/studio/creatives/{id}/captions.vtt?node_id=&seq=Descarcă subtitrările în format WebVTT
POST/v1/studio/creatives/{id}/adaptAdaptează la preseturi de platformă (dimensiuni + zone sigure) → versiune nouă
POST/v1/studio/creatives/{id}/lint/fixCorectează automat problemele lint (gratuit; tură de agent plătită, opțională)
GET/v1/studio/creatives/{id}/controlsModelul panoului de ajustare: un control pentru fiecare variabilă
GET/v1/studio/creatives/{id}/locales?locale=&units=Limbile: progresul pe fiecare limbă și, la cerere, unitățile de traducere
POST/v1/studio/creatives/{id}/localizeTraducere în cel mult 10 limbi → versiune nouă (facturată pe tokeni)
POST/v1/studio/creatives/{id}/locales/{locale}/forkCreație de sine stătătoare într-o singură limbă (201)
POST/v1/studio/creatives/{id}/templateSalvează o creație ca șablon privat (201)
GET/v1/studio/templates?kind=&limit=Șabloanele tale, cele mai noi primele
GET/v1/studio/templates/{id}Un șablon cu documentul lui
DELETE/v1/studio/templates/{id}Șterge un șablon
GET/v1/studio/creatives/{id}/export.fcpxml?format=&seq=Pune în coadă o arhivă cu proiect FCPXML (202)
GET/v1/studio/creatives/{id}/export.lottie?format=&formats=&seq=&container=&download=Export Lottie JSON / dotLottie, gratuit și gata imediat (200)
GET/v1/studio/exports/{id}Starea exportului; URL semnat al arhivei după succeeded
POST/v1/studio/documents/{id}/creativeEditează ca o creație o prezentare, un document sau o foaie de calcul (201 sau 200 dacă e deja legată)
POST/v1/studio/creatives/{id}/buildGenerează dintr-o creație o versiune nouă PPTX / DOCX / XLSX
POST/v1/studio/creatives/{id}/sharesCreează un link de vizualizare sau de revizuire (201, tokenul apare o singură dată) — vezi Partajare, revizuire și remix
GET/v1/studio/creatives/{id}/sharesLinkurile de partajare active
DELETE/v1/studio/creatives/{id}/shares/{shareId}Revocă un link de partajare (204)
GET/v1/studio/creatives/{id}/reviewStarea revizuirii și versiunea aprobată
POST/v1/studio/creatives/{id}/reviewCere o revizuire sau revino la ciornă
GET/v1/studio/creatives/{id}/review/threads?status=Firele de comentarii ale revizuirii
POST/v1/studio/creatives/{id}/review/threadsFir nou de comentarii ancorat (201)
POST/v1/studio/creatives/{id}/review/threads/{threadId}/repliesRăspunde într-un fir (201)
POST/v1/studio/creatives/{id}/review/threads/{threadId}/resolveRezolvă un fir
POST/v1/studio/creatives/{id}/review/threads/{threadId}/reopenRedeschide un fir
DELETE/v1/studio/creatives/{id}/review/comments/{commentId}Șterge un comentariu de revizuire (204)
POST/v1/studio/shares/{token}/remixCopiază o creație partajată în contul tău (201)

Documentul

{
  "schemaVersion": 1,
  "kind": "video",
  "title": "Teaser de lansare",
  "formats": [{ "id": "16x9", "width": 1920, "height": 1080 }],
  "defaultFormat": "16x9",
  "fps": 30,
  "brand": { "tokens": { "primary": "#ff5500" } },
  "vars": { "headline": { "type": "text", "value": "Salut, lume" } },
  "assets": { "3f2b8c1e-0000-4000-8000-000000000001": { "kind": "image", "mime": "image/png" } },
  "scenes": [
    {
      "id": "intro",
      "orderKey": "V",
      "durationMs": 4000,
      "nodes": [
        {
          "id": "title",
          "type": "text",
          "orderKey": "V",
          "layout": { "x": 200, "y": 400, "w": 1400, "h": 200 },
          "anim": [{ "preset": "fade-in" }],
          "props": { "spans": [{ "text": { "$var": "headline" } }], "size": 96, "color": { "$token": "primary" } }
        }
      ]
    }
  ],
  "audio": { "tracks": [] }
}
  • Scenele rulează în ordinea orderKey; o scenă video durează între 0,5 și 30 s, iar un video cel mult 180 s.
  • Nodurile sunt text, shape, image, video, chart, captions, group și html_block, fiecare cu un id, un layout și, opțional, timing, anim (preseturi precum fade-in, slide-up, ken-burns, typewriter sau cadre cheie) și suprascrieri per format (overrides).
  • Fișierele media se referă la asset-urile tale prin id. brand.tokens și vars se referă ca { "$token": "…" } și { "$var": "…" }.
  • Un document are cel mult 2 MB de JSON canonic (413 too_large).

Fără doc, POST /v1/studio/creatives îți dă un document gol pentru tipul ales: un video 16:9 cu o scenă goală de 5 s, o imagine 1080×1080, o prezentare 16:9 cu un slide sau un document / o foaie de calcul goală.

curl https://ai.codai.ro/v1/studio/creatives \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "video", "title": "Teaser de lansare" }'

Editarea prin operații

Trimite operațiile față de versiunea pe care ai citit-o (base_seq). Un lot se aplică în întregime sau deloc.

curl https://ai.codai.ro/v1/studio/creatives/$ID/ops \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base_seq": 3,
    "intent": "Titlu mai mare, fundal nou",
    "ops": [
      { "op": "setProps", "target": "title", "props": { "size": 120 } },
      { "op": "updateScene", "target": "intro", "patch": { "background": "#101010" } }
    ]
  }'
{ "seq": 4, "doc_hash": "9a3f…", "changed": ["title", "intro"], "warnings": [], "rebased": false }

Cele 25 de operații: setProps, setLayout, setTiming, setAnim, insertNode, removeNode, moveNode, addScene, removeScene, updateScene, moveScene, setVar, removeVar, setAsset, replaceAsset, setAudioClip, removeAudioClip, setBrand, restyle, adaptFormat, setControl, setLocaleText, setLocale, setLocalization și setMeta. Traducerile se editează cu setLocaleText, setLocale și setLocalization (vezi Localizare). Adaptarea la o platformă este un singur adaptFormat (vezi Platforme); descrierea felului în care se editează o variabilă în panoul de ajustare este un singur setControl (vezi Panoul de ajustare). Schimbarea tokenilor de brand este un singur setBrand; reaplicarea unui brand pe culorile și fonturile literale este un singur restyle (vezi Restilizarea unei creații); înlocuirea unei imagini peste tot este un singur replaceAsset.

Editări simultane

Dacă altcineva a editat creația după ce ai citit versiunea base_seq, gateway-ul îți rebazează operațiile: dacă niciuna dintre țintele și proprietățile pe care le atingi nu s-a schimbat între timp, se aplică peste versiunea curentă (rebased: true). Altfel primești 409 cu ce s-a suprapus — reîncarcă și încearcă din nou:

{
  "error": {
    "code": "stale",
    "type": "invalid_request_error",
    "message": "The creative changed since base_seq and 1 change(s) overlap; reload and retry.",
    "head_seq": 5,
    "conflicts": [{ "target": "title", "head_prop": "props.size", "prop": "props.size" }]
  }
}

Operații respinse

O operație invalidă, un id necunoscut sau un nod blocat dau 400 cu code-ul operației (invalid, not_found, locked, duplicate_id), path din operație sau document și op_index. Nu se salvează nimic. Trimite force: true ca să editezi intenționat noduri și variabile blocate.

Citire pentru agenți

Documentele mari nu trebuie trimise întregi. outline întoarce câte o linie pentru fiecare scenă și nod (id-uri, tipuri, etichete, text tăiat la 60 de caractere, momente); find întoarce id-uri de noduri după text, tip, scenă, asset sau blocare; subtree întoarce JSON-ul complet doar pentru id-urile cerute.

Versiuni

Fiecare editare este o versiune; istoricul doar crește. revert adaugă o versiune nouă cu un document mai vechi (intent: "revert to N"), deci și anularea, și refacerea sunt reveniri. fork pornește o creație nouă din orice versiune. GET …?seq=N citește o versiune veche.

Previzualizare și randare

GET …/compiled întoarce compoziția HyperFrames deterministă, cu asset-urile tale ca URL-uri semnate valabile o oră, gata pentru un iframe srcdoc (Cache-Control: private, no-store). POST …/render compilează versiunea curentă și pune în coadă un codai-render obișnuit:

curl https://ai.codai.ro/v1/studio/creatives/$ID/render \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "format": "16x9", "quality": "draft" }'

Răspunde 202 cu render_id; interoghează-l cu GET /v1/studio/renders/{id}, care raportează creative_id. Randarea se facturează ca orice randare, o singură dată, după ce a reușit. Formatul trebuie să fie 1920×1080, 1080×1920, 1080×1080 sau 1080×1350 (4:5). Randarea funcționează pentru creațiile video (celelalte tipuri răspund 400 not_supported_for_kind); compilarea previzualizează și creațiile de tip prezentare, document și foaie de calcul (vezi Documente).

Randările sunt compuse din segmente per scenă: scenele care nu s-au schimbat de la o randare anterioară se refolosesc, așa că o nouă randare după o editare mică randează doar scenele atinse (taxarea rămâne pe secundă de ieșire). Dacă randezi din nou exact aceeași versiune, același format, aceeași calitate și aceleași fps, răspunsul este imediat 200 cu randarea existentă (cached: true, status: "succeeded") și nu se taxează nimic; corpul 202 al unei randări noi are cached: false.

ai_label (corner, end_card sau none) imprimă în video o etichetă IA vizibilă, ca la randări. Dacă lipsește, este corner când creația folosește narațiune TTS sau imagini generate cu IA, altfel none; none cu narațiune TTS răspunde 400 ai_label_required. Conform Regulamentului UE privind IA (art. 50 alin. 4), tu, ca implementator, rămâi responsabil de etichetarea deepfake-urilor pe care le publici.

Cadre statice

POST …/frame întoarce cadre PNG statice ale creației la cel mult 6 momente, pentru un format (implicit formatul implicit al documentului) și o versiune (implicit cea curentă):

curl https://ai.codai.ro/v1/studio/creatives/$ID/frame \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "t_ms": [0, 1500, 4200], "format": "16x9", "exact": false }'
CâmpSemnificație
t_ms1–6 momente în milisecunde, fiecare sub durata totală a creației (obligatoriu)
formatUn formats[].id (implicit formatul implicit al documentului)
exactfalse (implicit): o captură rapidă a HTML-ului compilat. true: cadre decupate dintr-o randare ciornă a scenelor care acoperă acele momente — identice la pixel cu MP4-ul; umple și cache-ul de scene pentru randările următoare
versionVersiunea documentului (seq); implicit cea curentă

Apelul așteaptă cel mult aproximativ 90 de secunde. Când cadrele sunt gata, răspunde 200:

{
  "object": "studio.creative.frames",
  "frame_job_id": "5e6f7a8b-0000-4000-8000-000000000040",
  "status": "succeeded",
  "frames": [{ "t_ms": 1500, "url": "https://…semnat, 1 h…", "width": 1920, "height": 1080, "cached": false }],
  "head_seq": 3,
  "billed_micro_usd": "0"
}

Altfel răspunde 202 cu { "object": "studio.creative.frame_job", "frame_job_id": "…", "status": "running", "head_seq": 3 }; interoghează GET /v1/studio/creatives/{id}/frames/{jobId} până întoarce același corp 200 (202 cât timp rulează, 404 pentru un job necunoscut, 502 frame_failed dacă a eșuat).

Cache. Cadrele deja randate pentru același document, format și moment vin din cache instantaneu și gratuit (cached: true); când toate cadrele vin din cache, frame_job_id este null.

Cotă și preț. 60 de joburi de cadre lansate pe utilizator pe zi UTC sunt gratuite; răspunsurile servite integral din cache sunt mereu gratuite și nu se numără. După aceea, cadrele rapide costă fix 0,025 $ (25 000 µUSD) pe job, iar cele exacte prețul randării ciornă (0,005 $ pe secundă de scene randate din nou, minimum 0,025 $). billed_micro_usd arată suma.

Erori. 400 invalid_request (corp invalid, mai mult de 6 momente, un moment în afara creației), 400 unsupported_format (cadrele exacte cer 1920×1080, 1080×1920, 1080×1080 sau 1080×1350; cele rapide cel mult 3840 px pe latură), 400 not_supported_for_kind, 429 too_many_frame_jobs (cel mult 3 joburi de cadre în lucru), 502 frame_failed, 503 renders_unavailable.

Comentarii pe cadru

POST …/comments transformă comentariul unui recenzent, fixat pe un cadru, într-o editare. O tură scurtă de agent pe server citește schița, nodul comentat (sau scena, ori scena care rulează la t_ms) și comentariul, apoi propune operații aplicate exact ca la POST …/ops, pe base_seq-ul tău:

curl https://ai.codai.ro/v1/studio/creatives/$ID/comments \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "node_id": "title", "t_ms": 1200, "comment": "Mărește titlul și fă-l alb", "base_seq": 3 }'
CâmpSemnificație
t_msMomentul cursorului la comentariu (obligatoriu)
commentCe trebuie schimbat, 1–2000 de caractere (obligatoriu)
base_seqhead_seq-ul citit ultima dată (obligatoriu; o bază veche e rebazată ca la /ops)
node_id / scene_idNodul sau scena selectată (implicit: scena care rulează la t_ms)
applytrue (implicit) scrie o versiune; false întoarce doar ops propuse

Răspunde 200 { "head_seq": 4, "applied_ops": 1, "summary": "Am mărit titlul.", "version": { "seq": 4, "doc_hash": "…" } }. O modificare suprapusă de la base_seq răspunde 409 stale (reîncarcă, reîncearcă); când comentariul nu cere o editare răspunsul este 422 no_edit, cu replica modelului în error.text; după 60 s, 504 timeout. Operațiile respinse primesc automat o rundă de reparare.

Preț. Facturat pe tokeni ca orice apel text codai (rânduri de utilizare codai::creative-comment, cache de prompt activ) — un comentariu obișnuit costă mult sub 0,01 $.

Variante A/B

POST …/variants cere unei ture scurte de agent pe server variante A/B ale creației la base_seq. Fiecare variantă schimbă doar aspectele din focus și este salvată ca o creație nouă derivată din base_seq (titlul "<titlu> — <etichetă>", operațiile aplicate ca versiunea 2 de agent), deci o poți deschide, edita, randa sau șterge ca pe orice creație. Originalul nu se schimbă până nu alegi o câștigătoare.

curl https://ai.codai.ro/v1/studio/creatives/$ID/variants \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "count": 3, "focus": ["hook", "cta"], "brief": "Black Friday, public tânăr", "base_seq": 4 }'
CâmpSemnificație
base_seqVersiunea din care se derivă (obligatoriu)
count1–5 variante (implicit 3)
focusOricare dintre hook (deschiderea), title, cta, visual (culori, așezare, imagini, mișcare), copy (implicit hook, title, cta)
briefIndicații opționale, cel mult 1000 de caractere
rendertrue (implicit, doar video) pune în coadă o randare draft pentru fiecare variantă
formatId-ul formatului pentru randările draft (implicit: formatul implicit al documentului)

Răspunde 200 { "group_id": "…", "base_seq": 4, "variants": [{ "creative_id": "…", "label": "Hook cu întrebare", "rationale": "…", "applied_ops": 2, "head_seq": 2, "render_id": "…" }] } fără să aștepte randările — interoghează fiecare render_id cu GET /v1/studio/renders/{id}. Propunerile invalide sunt ignorate (o rundă automată de reparare); când niciuna nu e validă răspunsul este 422 no_edit; după 90 s, 504 timeout. O randare care nu a putut fi pusă în coadă lasă varianta cu render_error.

GET …/variants întoarce ultimul grup: fiecare variantă cu status (candidate, winner, rejected) și starea salvată a randării draft (poster_url și url după ce a reușit).

POST …/variants/{variantId}/pick cu { "base_seq": <versiunea curentă a originalului> } aplică operațiile câștigătoarei pe original exact ca POST …/ops (autor agent, intenție variant: <etichetă>; o bază veche e rebazată, o modificare suprapusă dă 409 stale) și marchează câștigătoarea winner, iar celelalte rejected. Răspunde { "head_seq", "version", "winner", "rebased" }.

Preț. Generarea este facturată pe tokeni ca orice apel text codai (rânduri de utilizare codai::creative-variants, cache de prompt activ — mult sub 0,05 $ pentru trei variante); fiecare randare draft se facturează la interogare ca orice randare draft (0,005 $ pe secundă de ieșire, minimum 0,025 $). Listarea și alegerea sunt gratuite.

Kituri de brand

Un kit de brand este un set reutilizabil de tokeni de brand — cinci roluri de culoare primary, secondary, accent, background, text, până la 12 culori suplimentare cu nume, familiile de fonturi heading și body și o rază de colț opțională — plus un logo opțional. Kiturile sunt salvate ca tokeni de design DTCG (formatul 2025.10), așa că le poți importa din instrumentele de design și le poți exporta către ele.

Formatul DTCG

{
  "$schema": "https://www.designtokens.org/schemas/2025.10/format.json",
  "color": {
    "$type": "color",
    "primary": { "$value": { "colorSpace": "srgb", "components": [0.004, 0.439, 0.502], "hex": "#017080" } },
    "secondary": { "$value": { "colorSpace": "srgb", "components": [0.137, 0.671, 0.737], "hex": "#23abbc" } },
    "accent": { "$value": { "colorSpace": "srgb", "components": [0.902, 0.718, 0.192], "hex": "#e6b731" } },
    "background": { "$value": { "colorSpace": "srgb", "components": [0.969, 0.973, 0.98], "hex": "#f7f8fa" } },
    "text": { "$value": { "colorSpace": "srgb", "components": [0.059, 0.067, 0.082], "hex": "#0f1115" } },
    "sand": { "$value": { "colorSpace": "srgb", "components": [0.949, 0.902, 0.82], "hex": "#f2e6d1" } }
  },
  "font": {
    "$type": "fontFamily",
    "heading": { "$value": ["Manrope"] },
    "body": { "$value": ["Inter", "Arial"] }
  },
  "radius": { "$type": "dimension", "base": { "$value": { "value": 8, "unit": "px" } } }
}
  • Cele cinci roluri de culoare sunt obligatorii. Culorile suplimentare au un nume format dintr-o literă mică urmată de cel mult 31 de litere mici, cifre sau - (cel mult 12 culori).
  • Importul este tolerant: $value al unei culori poate fi și un șir "#abc" / "#aabbcc", { "hex" } sau { "colorSpace": "srgb", "components" }; $type poate sta pe token sau pe grupul lui; $value al unui font poate fi un singur șir. Kiturile salvate folosesc mereu forma canonică de mai sus.
  • $value al unui font enumeră 1–4 nume de familii (o listă goală înseamnă necunoscut); radius are 0–200 px și este opțional.
  • JSON-ul canonic are cel mult 16 KB. Un fișier de tokeni invalid primește 400 invalid_tokens, cu problemele în error.details.issues.

Fiecare kit răspunde cu dtcg, cu tokenii plați scriși în creații (mai jos), cu contrast și cu updated_at; câmpurile sunt { "id", "name", "source_url", "dtcg": { … }, "tokens": { … }, "contrast": [ … ], "logo_asset_id", "logo_url", "logo_mime_type", "created_at", "updated_at" }, adică identificatorul, numele, sursa, tokenii, contrastul și logoul.

Contrast. contrast enumeră raportul de contrast WCAG pentru cinci perechi fixe — text pe fundal, primar pe fundal, fundal pe primar, accent pe fundal și secundar pe fundal — sub forma { "foreground", "background", "ratio", "aa" } (ratio rotunjit la 2 zecimale, aa este true de la 4,5:1 în sus).

Tokeni plați. Creațiile păstrează o hartă plată în brand.tokens: cele cinci roluri de culoare, headingFont și bodyFont (prima familie, sau ""), fiecare culoare suplimentară sub numele ei, radius (px) când există, plus aliasurile de document color.bg (= fundal), color.fg (= text), color.accent (= primar), font.heading și font.body, ca și creațiile convertite din prezentări, documente și foi de calcul să urmeze kitul.

Creare, import, editare, export

POST /v1/studio/brand-kits creează manual un kit. Trimiți name (1–100 caractere) și exact unul dintre tokens (harta plată: toate cele cinci culori ca #rrggbb, plus headingFont / bodyFont de cel mult 100 de caractere) sau dtcg (un fișier DTCG de importat); opțional logo_asset_id (unul dintre asset-urile tale image gata procesate, altfel 404 Asset not found.) și source_url (https). Răspunde 201 cu kitul.

curl https://ai.codai.ro/v1/studio/brand-kits \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme", "tokens": { "primary": "#e4002b", "secondary": "#1d3557", "accent": "#ffb703",
        "background": "#ffffff", "text": "#111111", "headingFont": "Manrope", "bodyFont": "Inter" } }'

PATCH /v1/studio/brand-kits/{id} editează un kit: oricare dintre name, tokens (parțial — combinat peste kitul curent), dtcg (înlocuiește tokenii) și logo_asset_id (null elimină logoul); cel puțin un câmp și niciodată tokens împreună cu dtcg. Răspunde 200 cu kitul; un kit care nu e al tău sau a fost șters dă 404. Creațiile care folosesc deja kitul își păstrează tokenii până când le restilizezi.

GET /v1/studio/brand-kits/{id}/dtcg descarcă kitul ca fișier DTCG (application/json, content-disposition: attachment; filename="<slug>.tokens.json"), cu "$extensions": { "ro.codai": { "kit_id", "name", "logo_asset_id" } } adăugat, gata pentru orice instrument de design care înțelege DTCG.

POST /v1/studio/brand-kits/extract construiește un kit din exact unul dintre url (o pagină publică https) sau logo_asset_id (o imagine de logo aflată deja în biblioteca ta); name este opțional în ambele cazuri.

curl https://ai.codai.ro/v1/studio/brand-kits/extract \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://acme.example/" }'

Dintr-un URL, gateway-ul descarcă pagina și până la 3 dintre foile ei de stil (fiecare redirecționare este verificată din nou contra adreselor private și interne; 10 s, 3 MB, doar HTML), adună candidații de culori, fonturi și logo (theme-color, proprietăți CSS personalizate, font-family, linkuri Google Fonts, imagini de logo, iconițe touch, og:image), iar o singură tură limitată a agentului de pe server alege o paletă coerentă (text lizibil pe fundal) și fonturile. Logoul ales (PNG, JPEG sau WebP de cel mult 5 MB — SVG nu se importă) este salvat în biblioteca ta Studio ca asset image.

Dintr-un logo ({ "logo_asset_id": "<id asset imagine>" }), imaginea trebuie să fie unul dintre asset-urile tale PNG, JPEG sau WebP gata procesate, de cel mult 5 MB (altfel 400 unsupported_media). O singură tură de viziune citește paleta și propune fonturi potrivite logoului; logoul devine logoul kitului, iar source_url este null.

Ambele răspund 201 cu kitul (salvat ca DTCG). Erori: 400 url_blocked (gazdă privată sau blocată), 400 unsupported_media, 422 fetch_failed (pagina nu a putut fi descărcată sau nu e HTML), 422 extract_failed (niciun kit valid), 504 timeout după 60 s.

Aplicarea unui kit

  • La creare — POST /v1/studio/creatives cu "brand_kit_id": "<id kit>": versiunea 1 are deja brand = { kitId, tokens } și logoul înregistrat în assets (fără o versiune în plus). Un kit necunoscut dă 404.
  • Pe o creație existentă — POST /v1/studio/creatives/{id}/restyle (mai jos).
  • Manual — trimite [{ "op": "setBrand", "kitId": "<id kit>", "tokens": { … } }] (plus un setAsset pentru logo) la POST …/ops; asta înlocuiește doar tokenii, culorile literale rămân cum sunt.

Nodurile folosesc tokenii ca { "$token": "primary" }. GET /v1/studio/brand-kits listează kiturile tale (cele mai noi primele), GET …/{id} citește unul, DELETE …/{id} îl șterge (creațiile își păstrează tokenii pe care îi au deja; logoul rămâne în bibliotecă).

Restilizarea unei creații

POST /v1/studio/creatives/{id}/restyle reaplică un brand pe o creație făcută înaintea kitului sau cu culori scrise direct în noduri:

curl https://ai.codai.ro/v1/studio/creatives/$ID/restyle \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "brand_kit_id": "<id kit>", "snap": true }'
CâmpSemnificație
brand_kit_id / tokensExact unul: unul dintre kiturile tale sau o hartă plată de tokeni
base_seqVersiunea citită (implicit: capul); o bază veche este rebazată ca la /ops
snaptrue mută orice altă culoare literală pe cea mai apropiată culoare din kit (implicit false)
fontsfalse lasă fonturile neatinse (implicit true)
forcetrue restilizează și nodurile blocate

Gateway-ul trimite o singură operație restyle (plus un setAsset pentru logoul kitului, dacă are unul) prin aceeași cale de editare ca POST …/ops (intenția restyle with brand kit <nume>) și răspunde 200 cu același corp ca /ops, inclusiv warnings. Regulile:

  1. Transformare în tokeni. Brandul nou este vechiul brand.tokens cu tokenii kitului deasupra. Fiecare #rrggbb literal din fundalurile scenelor și din proprietățile nodurilor (inclusiv opririle de gradient) egal cu valoarea unui token vechi devine o referință de token, de exemplu { "$token": "primary" }, deci preia culoarea nouă.
  2. Aliniere (snap). Cu snap: true, orice altă culoare literală devine o referință la cel mai apropiat, perceptual, dintre cele cinci roluri de culoare (distanță OKLab). Culorile cu transparență (#rrggbbaa) rămân neschimbate și sunt numărate într-un avertisment.
  3. Fonturi după rol. Un font literal egal cu un token de font vechi devine acel token. Orice alt font literal pe un nod text, listă sau tabel devine headingFont pentru titluri (rolurile title, subtitle, heading1–heading4, column-heading, stat-value sau text de mărime 48 și peste în afara documentelor) și bodyFont în rest.
  4. Nodurile blocate (sau cele aflate într-un grup blocat) sunt sărite cu avertismentul restyle skipped locked node <id>, în afară de cazul force: true.

Aceeași operație funcționează în orice lot trimis la /ops:

{ "op": "restyle", "kitId": "<kit id>", "tokens": { "primary": "#e4002b", "headingFont": "Manrope" }, "snap": false, "fonts": true }

Verifică apoi rezultatul cu cadre statice și cu lint (contrast).

SDK-uri și unelte de agent

const kit = await codai.brandKits.create({ name: 'Acme', dtcg });
await codai.brandKits.update(kit.id, { tokens: { accent: '#ffb703' } });
const tokensFile = await codai.brandKits.exportDtcg(kit.id);
const fromLogo = await codai.brandKits.extract({ logo_asset_id: logoAssetId });
const restyled = await codai.creatives.restyle(c.id, { brand_kit_id: kit.id, snap: true });
tokens_file = client.brand_kits.export_dtcg(kit["id"])
from_logo = client.brand_kits.extract(logo_asset_id=logo_asset_id)
restyled = client.creatives.restyle(c["id"], brand_kit_id=kit["id"], snap=True)

Agenții de pe desktop, telefon și VS Code folosesc creative_brand_extract (url sau logo_asset_id), creative_brand_kit_save (fără brand_kit_id creează un kit din name + tokens sau dtcg; cu el, editează acel kit) și creative_restyle (creative_id, brand_kit_id sau tokens, snap, fonts). creative_edit acceptă și operația restyle.

Preț. Extragerea este facturată pe tokeni ca orice apel text codai (rând de utilizare codai::brand-kit-extract, cache de prompt activ — de obicei mult sub 0,01 $, dintr-un URL sau dintr-un logo). Crearea, editarea, exportul, listarea, citirea, ștergerea, aplicarea kiturilor și restilizarea sunt gratuite.

Subtitrări

POST /v1/studio/creatives/{id}/captions adaugă subtitrări automate unei creații video: transcrie un clip de voce cu momentul fiecărui cuvânt, corectează ortografia după scenariu când narațiunea este a ta și inserează un nod captions stilizat în zona sigură a formatului implicit (deasupra interfeței platformei când formatul este legat de o platformă, vezi Platforme).

curl https://ai.codai.ro/v1/studio/creatives/$ID/captions \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "preset": "karaoke-pop" }'
CâmpSemnificație
clip_idClipul audio de subtitrat (implicit: primul clip de voce)
presetbold-pill (implicit), clean-outline, karaoke-pop sau minimal
node_idId-ul nodului de subtitrare (implicit captions, apoi captions-2…); un nod de subtitrare existent cu acest id este înlocuit
scriptTextul exact al narațiunii, cel mult 20 000 de caractere (implicit: textul din care a fost generat asset-ul de voce)
languageLimba transcrierii (implicit: locale al documentului)
retranscribetrue ignoră cuvintele din cache și transcrie din nou
base_seq / forceCa la /ops (implicit: capul)

Momentele cuvintelor. Sunetul clipului (un asset speech, music sau video al tău, gata procesat, de cel mult 25 MB) este transcris de Whisper, cu câte un marcaj de timp pentru fiecare cuvânt. Cuvintele sunt relative la clip: se scade inMs-ul lui, iar cuvintele din afara intervalului tăiat al clipului sunt eliminate.

Corectarea după scenariu. Când clipul este narațiunea ta — vocea generată în Studio își păstrează textul sursă — sau când trimiți script, transcrierea este aliniată cuvânt cu cuvânt cu scenariul: momentele vin din transcriere, ortografia (nume, diacritice, punctuație) din scenariu. Când scenariul nu se potrivește cu ce s-a spus, transcrierea rămâne neschimbată (corrected: false).

Cuvinte în cache. Cuvintele sunt salvate pe asset-ul de voce (meta.caption_words), așa că o nouă subtitrare a aceluiași clip — alt preset, altă creație care folosește același asset, un node_id nou — le refolosește gratuit (cached: true). Trimite retranscribe: true ca să forțezi o transcriere nouă.

Editarea trece pe aceeași cale ca POST …/ops (un insertNode în scena care conține începutul clipului sau un setProps pe nodul existent) și răspunde 200 cu corpul de la /ops, plus câmpurile { "node_id", "words", "cached", "corrected", "cost_micro_usd" }.

Preseturi. Mărimile sunt o fracțiune din latura scurtă a formatului:

PresetStilMărimea textuluiCuvinte pe rândCulori
bold-pillFundal rotunjit sub text5,5 %4Alb
clean-outlineText cu contur5 %6Alb
karaoke-popEvidențiere cuvânt cu cuvânt6 %3Alb, evidențiere cu tokenul accent (sau #ffd60a)
minimalText cu contur4 %8Alb

Schimbarea ulterioară a presetului este un singur setProps pe nodul de subtitrare (stil, cuvinte pe rând, mărime, culori).

Export SRT și VTT. GET /v1/studio/creatives/{id}/captions.srt și GET …/captions.vtt descarcă subtitrările pe cronologia documentului (SubRip text/plain sau WebVTT text/vtt, content-disposition: attachment), rând cu rând, exact cum le arată videoclipul. node_id alege un singur nod de subtitrare (implicit: toate, combinate în timp), iar seq o versiune (implicit: capul). Fără subtitrări răspunsul este 404 No captions.. Exportul este gratuit.

Erori. 400 unsupported_kind (nu este o creație video), 400 unsupported_media (sunet de peste 25 MB), 404 Clip not found., 404 Asset not found., 403 residency_unavailable (cu rezidența datelor în UE transcrierea nu este disponibilă), 503 transcription_unavailable.

Preț. O transcriere nouă costă aproximativ 0,006 $ pe minut de sunet al clipului (rotunjit în sus la minut, rând de utilizare codai::captions, același tarif ca transcrierea prin /v1/audio). Cuvintele din cache, corectarea după scenariu, preseturile și exportul SRT/VTT sunt gratuite.

Platforme

Un preset de platformă înseamnă o dimensiune de ieșire plus partea din cadru acoperită de interfața platformei (bara de descriere, butoanele de acțiune, bara de progres). POST /v1/studio/creatives/{id}/adapt adaptează o creație video sau imagine la una sau mai multe platforme, într-o singură versiune:

curl https://ai.codai.ro/v1/studio/creatives/$ID/adapt \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "platforms": ["tiktok", "feed-portrait", "youtube"] }'

Zona sigură este partea din fiecare latură lăsată liberă de text; mărimea minimă a textului este în pixeli de ieșire:

PlatformăEtichetăDimensiuneZonă sigură (sus / dreapta / jos / stânga)Text minim
tiktokTikTok1080×19208 % / 13 % / 20 % / 5 %36 px
reelsInstagram Reels1080×19207 % / 10 % / 18 % / 5 %36 px
shortsYouTube Shorts1080×19207 % / 12 % / 17 % / 5 %36 px
feed-squareFeed 1:11080×10805 % pe fiecare latură32 px
feed-portraitFeed 4:51080×13505 % pe fiecare latură32 px
youtubeYouTube 16:91920×10805 % pe fiecare latură28 px

Zonele sunt aproximări intenționat prudente ale ghidurilor publicate de platforme, care se schimbă des.

CâmpSemnificație
platforms1–6 id-uri de platformă distincte (obligatoriu)
focalnone (implicit) sau vision — vezi mai jos
base_seq / forceCa la /ops (implicit: capul); force adaptează și nodurile blocate

Gateway-ul trimite câte o operație adaptFormat pentru fiecare platformă, pe aceeași cale de editare ca /ops. Pentru fiecare platformă:

  1. Un format de aceeași dimensiune este refolosit; altfel se adaugă unul cu id-ul de aspect al presetului (9x16, 1x1, 4x5, 16x9, cu sufixul -2, -3… când id-ul aparține unui format de altă dimensiune), iar legătura se notează în meta.platforms[<id format>].
  2. Imaginile și videoclipurile pe tot cadrul rămân pe tot cadrul; textele, subtitrările și grupurile sunt mutate și încadrate în zona sigură a platformei (micșorate la lățimea zonei când sunt mai late). Formatul implicit se modifică direct pe nod, orice alt format prin suprascrieri per format (overrides).
  3. Textul sub minimul platformei primește o mărime mai mare doar în acel format.
  4. Nodurile blocate sunt sărite cu un avertisment (în afară de cazul force: true).

Aceeași operație funcționează în orice lot trimis la /ops:

{ "op": "adaptFormat", "platform": "reels", "focal": { "hero": { "x": 0.62, "y": 0.4 } } }

focal (opțional) fixează punctul de interes (0–1 pe fiecare axă) al nodurilor imagine și video în acel format, ca decuparea să îl păstreze în cadru. Fără el, reîncadrarea este deterministă și centrată.

Punct focal inteligent (vision). "focal": "vision" este o opțiune la cerere, facturată: o tură de vision analizează imaginile de nivel superior care acoperă tot cadrul (asset-uri PNG, JPEG sau WebP ale tale, gata procesate, de cel mult 5 MB, maximum 6) și întoarce un punct focal pentru fiecare, folosit apoi de toate formatele noi. Rulează o singură dată pe apel, pentru toate platformele, cu o limită de 60 s; dacă eșuează, adaptarea are loc oricum, cu decupări centrate și avertismentul vision focal unavailable.

Răspunde 200 cu corpul de la /ops, plus câmpurile { "formats": [{ "platform", "format_id", "width", "height" }], "focal": { "<id nod>": { "x", "y" } } | null }. Erori: 400 unsupported_kind (doar creații video și imagine), 400 invalid_request.

Randări în 4:5. Randările și cadrele exacte acceptă formatul feed-portrait (1080×1350) alături de 1920×1080, 1080×1920 și 1080×1080, așa că o creație adaptată pentru un feed 4:5 se randează direct.

Preț. Adaptarea este gratuită. Doar focal: "vision" este facturat pe tokeni ca orice apel vision codai (rând de utilizare codai::adapt-focal, cache de prompt activ — de obicei mult sub 0,01 $ pe apel).

Lint și corecturi

POST /v1/studio/creatives/{id}/lint rulează verificări statice, fără randare. Erorile blochează randarea; avertismentele îți sunt arătate ție și agenților. Fiecare problemă are forma { "code", "severity", "message", "target", "value"?, "format"?, "fixable"? } — format numește formatul la care se referă o problemă specifică unui format, iar fixable: true marchează problemele pe care corectura automată gratuită le poate rezolva — iar răspunsul adaugă fixable, numărul acestor probleme.

CodSeveritateCorectabilCe înseamnă
low_contrasteroarenuContrastul textului sub pragul WCAG (4,5:1 sau 3:1 pentru text mare)
outside_safe_areaavertismentnuText sau subtitrări în afara zonei sigure (zona platformei când formatul implicit este legat de o platformă)
empty_textavertismentnuUn nod text fără text
captions_no_wordseroarenuUn nod de subtitrare fără cuvinte
captions_no_clipavertismentnuSubtitrări care trimit la un clip audio lipsă
missing_asseteroarenuUn id de asset neînregistrat în document
scene_zero_durationeroare / avertismentnuO scenă de 0 ms (eroare) sau un nod care nu apare niciodată (avertisment)
too_longeroarenuDurata totală depășește 180 s
chart_no_svgavertismentnuUn grafic fără SVG randat în prealabil
unresolved_refavertismentnuO referință la o variabilă sau la un token necunoscut
empty_listavertismentnuO listă fără text în elemente
off_brand_coloravertismentdaO culoare literală din afara paletei de brand (nuanțele neutre de gri nu sunt semnalate niciodată)
outside_platform_safe_zoneavertismentdaText sub interfața unei platforme, într-un format legat de acea platformă
text_too_smallavertismentdaText sub pragul de lizibilitate al unui format (value = px)
missing_altavertismentnuO imagine dintr-o prezentare sau dintr-un document fără text alternativ
missing_captionsavertismentnuUn clip de voce fără nod de subtitrare — folosește Subtitrări
reading_speedavertismentnuUn rând de subtitrare mai rapid de 20 de caractere pe secundă (value = caractere pe secundă)
flashingeroare / avertismentda / nuO animație care pâlpâie de peste 3 ori pe secundă (eroare, corectabilă) sau cel puțin 4 scene consecutive mai scurte de 334 ms (avertisment)

Pragul de lizibilitate este minimul platformei (vezi Platforme), altfel 2,5 % din latura scurtă a formatului.

Corecturi automate. POST /v1/studio/creatives/{id}/lint/fix transformă problemele corectabile într-un singur lot de operații, aplicat ca la /ops:

curl https://ai.codai.ro/v1/studio/creatives/$ID/lint/fix \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "codes": ["off_brand_color", "text_too_small"] }'
CâmpSemnificație
codesCorectează doar aceste coduri (implicit: toate codurile corectabile)
agenttrue adaugă o tură plătită de agent pentru problemele rămase (implicit false)
base_seq / forceCa la /ops (implicit: capul); force corectează și nodurile blocate

Corecturile deterministe sunt gratuite: culorile din afara brandului devin un { "$token" } al celei mai apropiate culori de brand, textul prea mic primește o mărime mai mare (sau un adaptFormat în formatele de platformă), textul aflat sub interfața unei platforme este readaptat cu adaptFormat, iar animațiile cu cadre cheie care pâlpâie sunt eliminate. Când nu există nimic de corectat, răspunde 200 fără să scrie o versiune (changed: []).

Cu agent: true, după lotul determinist o tură scurtă de agent pe server primește problemele rămase și schița și propune operații (text alternativ, contrast, viteză de citire…), aplicate pe aceeași cale de editare, cu o rundă de reparare. Dacă agentul eșuează primești totuși rezultatul determinist și avertismentul agent fix unavailable.

Răspunde 200 cu corpul de la /ops, plus câmpurile { "fixed": [{ "code", "target" }], "remaining": [ … ], "agent": { "ops", "cost_micro_usd" } | null } — remaining este rezultatul lint de după corectură.

Preț. Lint-ul și corecturile deterministe sunt gratuite. agent: true este facturat pe tokeni ca orice apel text codai (rând de utilizare codai::lint-fix, cache de prompt activ — de obicei mult sub 0,01 $).

Panoul de ajustare

Editoarele din hub, desktop și telefon arată un panou de ajustare pentru variabilele documentului (vars): câte un selector de culoare, un cursor, un comutator, o listă de opțiuni, un câmp text sau un selector de imagine pentru fiecare variabilă. Variabilele își păstrează tipul și valoarea în vars; meta.controls[<cheie variabilă>] descrie doar cum se editează fiecare.

{ "op": "setControl", "key": "accent", "control": { "kind": "select", "label": "Accent", "group": "Colours", "options": [{ "value": "#e4002b", "label": "Red" }, { "value": "#1d3557", "label": "Navy" }] } }
Tip de controlSe potrivește variabilelor de tipCâmpuri suplimentare
texttext, numbermultiline
colorcolor—
slidernumbermin și max (obligatorii, min < max), step
toggleboolean—
selecttext, number, coloroptions (obligatoriu, 1–24 elemente { "value", "label"? })
imageimage—

Orice control acceptă și label (cel mult 100 de caractere) și group (cel mult 60 de caractere, grupează panoul). setControl cu "control": null șterge controlul. Ca orice operație respinsă, un control care nu se potrivește tipului variabilei răspunde 400 cu codul invalid, iar o variabilă necunoscută 400 cu codul not_found.

GET /v1/studio/creatives/{id}/controls întoarce modelul panoului, { "head_seq", "controls": [{ "key", "label", "control", "value", "type", "locked" }] }: câte o intrare pentru fiecare variabilă, cu controlul salvat când este valid, altfel unul dedus din tip (culoare → color, boolean → toggle, imagine → image, număr → slider în jurul valorii curente, text → text). Schimbarea unei valori este o operație setVar obișnuită pe POST …/ops; editarea unui control este setControl. Citirea și editarea controalelor sunt gratuite.

SDK-uri și unelte de agent (subtitrări, platforme, lint, controale)

const cap = await codai.creatives.captions(c.id, { preset: 'bold-pill' });
const srt = await codai.creatives.captionsSrt(c.id, { node_id: cap.node_id });
const adapted = await codai.creatives.adapt(c.id, { platforms: ['tiktok', 'feed-portrait'], focal: 'vision' });
const fixed = await codai.creatives.lintFix(c.id, { agent: true });
const { controls } = await codai.creatives.controls(c.id);
cap = client.creatives.captions(c["id"], preset="bold-pill")
vtt = client.creatives.captions_vtt(c["id"])
adapted = client.creatives.adapt(c["id"], ["tiktok", "feed-portrait"], focal="vision")
fixed = client.creatives.lint_fix(c["id"], agent=True)
controls = client.creatives.controls(c["id"])["controls"]

Agenții de pe desktop, telefon și VS Code folosesc creative_captions (creative_id, clip_id, preset, script, language), creative_adapt (creative_id, platforms, focal) și creative_lint_fix (creative_id, codes, agent); creative_lint enumeră codurile noi, iar creative_edit acceptă operațiile adaptFormat și setControl.

Localizare

O creație poate avea mai multe limbi. Documentul sursă rămâne așa cum este; fiecare limbă în plus este un set de suprascrieri păstrate în document sub meta.localization, iar fiecare citire care primește un locale (previzualizare, randare, cadre statice, subtitrări, lint) le aplică pe loc. Când o limbă este gata să trăiască separat, o copiezi într-o creație de sine stătătoare.

Modelul de suprascrieri

{
  "meta": {
    "localization": {
      "source": "ro",
      "glossary": ["codai", "Black Friday"],
      "locales": {
        "en": {
          "tone": "informal",
          "texts": { "title:spans.0": { "t": "Autumn sale", "h": "3fa2c9d1e0b4a7f6", "by": "agent" } },
          "audio": { "voice-1": "<id asset de voce>" },
          "captions": { "captions": [{ "text": "Autumn", "startMs": 0, "endMs": 420 }] }
        }
      }
    }
  }
}
  • source este limba documentului însuși (implicit: locale al documentului, altfel en). Limbile sunt etichete BCP-47 (ro, en, de-DE, pt-BR), normalizate la intrare (ro_ro → ro-RO).
  • texts asociază cheii unei unități de traducere traducerea ei t, amprenta h a textului sursă din care a fost făcută și by (agent sau user). Când textul sursă se schimbă ulterior, amprenta nu se mai potrivește și traducerea devine învechită — este folosită în continuare până o reîmprospătezi.
  • audio înlocuiește asset-ul unui clip de voce pentru acea limbă, iar captions înlocuiește cuvintele unui nod de subtitrare (vezi Voce și subtitrări).

Unități de traducere. Fiecare text literal din document este o unitate, adresată printr-o cheie stabilă:

Tip de unitateCheieUnde
text<id nod>:spans.<i>Un segment de text
list<id nod>:items.<i>.spans.<j>Un segment dintr-un element de listă
table, cell<id nod>:columns.<c>, <id nod>:rows.<r>.<c>, <id nod>:captionAntetele, celulele text și legenda tabelului (grilele din foi: antete și celule text; formulele, datele și numerele rămân neschimbate)
chart<id nod>:spec.title, spec.alt, spec.categories.<i>, spec.series.<i>.nameEtichetele graficului
alt, caption<id nod>:alt, <id nod>:captionTextul alternativ și legenda imaginii
html, param<id nod>:html.<i>, <id nod>:params.<cheie>Fragmentele de text ale unui bloc HTML (etichetele rămân neatinse) și parametrii lui text
captions<id nod>:wordsCuvintele subtitrării, unite prin spații
notesscene:<id scenă>:notesNotele vorbitorului
titledoc:titleTitlul creației
varvar:<cheie>O variabilă text
narrationclip:<id clip>Textul narațiunii unui clip de voce (folosit doar cu voice: true)

Textele goale, numerele simple și segmentele { "$var" } sunt sărite (variabila este propria ei unitate); un document are cel mult 2000 de unități. Unitățile text au un buget de lungime (max_chars), calculat din dimensiunea casetei și mărimea fontului în formatul implicit, astfel încât traducerile care ar depăși caseta sunt semnalate.

Citirea limbilor

GET /v1/studio/creatives/{id}/locales întoarce gratuit starea fiecărei limbi:

{
  "head_seq": 7,
  "source": "ro",
  "glossary": ["codai"],
  "total": 18,
  "locales": [{ "locale": "en", "tone": "informal", "translated": 17, "missing": 1, "stale": 2, "voice": 0 }]
}

Cu ?locale=en&units=1 adaugă units (offset, limit cel mult 500, implicit 200) și units_total: câte o intrare pentru fiecare unitate, cu key, node_id, kind, text (sursa), max_chars, traducerea curentă translation (sau null), by, stale și locked. O limbă necunoscută răspunde 404 Locale not found..

Traducerea cu modelul

POST /v1/studio/creatives/{id}/localize traduce creația în cel mult 10 limbi într-un singur apel și salvează totul ca o singură versiune nouă (autor agent, intenție localize: en, de):

curl https://ai.codai.ro/v1/studio/creatives/$ID/localize \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "locales": ["en", "de"], "tone": "informal", "glossary": ["codai", "Black Friday"] }'
CâmpSemnificație
locales1–10 limbi țintă distincte, niciodată limba sursă (obligatoriu)
toneformal (adresare politicoasă, de exemplu „dumneavoastră”, „Sie”) sau informal („tu”, „du”); salvat pentru fiecare limbă
glossaryCel mult 100 de termeni (fiecare de cel mult 100 de caractere) păstrați exact cum sunt scriși; salvat în document
only_missingtrue (implicit) traduce doar unitățile fără o traducere actuală; false traduce totul din nou
voicetrue (doar video) reînregistrează și narațiunea și resincronizează subtitrările pentru fiecare limbă — vezi mai jos
base_seq / forceCa la /ops (implicit: capul); force traduce și nodurile blocate

Regulile pe care le urmează modelul: substituenții, numerele, adresele URL și emoji-urile se păstrează; termenii din glosar și numele și sloganul brandului din kitul de brand al creației sunt copiate întocmai; tonul este respectat; fiecare traducere rămâne în limita max_chars a unității (mai scurt este în regulă). Unitățile sunt trimise în tranșe (cel mult 300 de unități / 30 000 de caractere pe tură de model, trei limbi în paralel); cheile necunoscute și răspunsurile goale sunt ignorate, iar un răspuns lipsă primește o rundă de reparare.

Răspunde 200 cu corpul de la /ops, plus câmpurile { "locales": [{ "locale", "translated", "remaining", "voice_clips" }], "cost_micro_usd" }. O limbă care nu a putut fi tradusă adaugă avertismentul translation unavailable for <locale>; când toate limbile au eșuat, răspunsul este 503 localize_unavailable. Erori: 400 invalid (o limbă greșită sau limba sursă), 400 nothing_to_translate (toate unitățile sunt deja traduse și actuale).

Preț. Traducerea este facturată pe tokeni ca orice apel text codai (rânduri de utilizare codai::localize, cache de prompt activ — o creație obișnuită costă mult sub 0,01 $ pe limbă). Citirea limbilor, editările manuale și copiile sunt gratuite.

Voce și subtitrări

Cu voice: true, pentru fiecare clip de voce care este propria ta narațiune Studio (un asset de voce gata, care și-a păstrat textul sursă), narațiunea este tradusă odată cu textele, reînregistrată cu aceleași setări de voce în limba țintă și înlocuită doar pentru acea limbă (audio). Când un nod de subtitrare afișează acel clip, vocea nouă este transcrisă, iar subtitrările traduse sunt resincronizate (captions). Narațiunea se facturează ca orice narațiune, iar transcrierea ca la Subtitrări (rând de utilizare codai::captions). Un clip care nu poate fi reînregistrat adaugă avertismentul voice unavailable for <clip> (<locale>); textele sunt traduse oricum.

Editarea manuală a traducerilor

Editările manuale sunt operații obișnuite pe POST …/ops:

{ "op": "setLocaleText", "locale": "en", "texts": { "title:spans.0": "Autumn sale", "doc:title": null } }
  • setLocaleText setează cel mult 500 de unități ale unei limbi (null șterge o traducere, care apoi contează ca lipsă); amprenta salvată este cea a textului sursă curent, deci unitatea devine din nou actuală. by are implicit valoarea user. O cheie necunoscută dă not_found, o unitate a unui nod blocat locked (fără force), iar limba sursă invalid.
  • setLocale setează tone al unei limbi, audio (id clip → id asset; asset-ul trebuie înregistrat cu setAsset) și captions (id nod de subtitrare → cuvinte); "remove": true șterge limba.
  • setLocalization setează limba sursă source a documentului, glosarul glossary și legătura forkOf; "reset": true șterge toate limbile.

Verificări

POST …/lint adaugă trei avertismente pentru creațiile traduse, fiecare cu locale:

  • missing_translation — unul pentru fiecare limbă, value = numărul de unități fără traducere.
  • stale_translation — o unitate al cărei text sursă s-a schimbat după traducere (cel mult 50 pe limbă; mesajul numește cheia).
  • text_overflow_locale — corectabil: o traducere mai lungă decât permite caseta de text în formatul implicit. POST …/lint/fix setează pe acel nod autoFit: "shrink", astfel încât textul se micșorează ca să încapă în orice limbă.

Trimite { "locale": "en" } la POST …/lint ca să verifici chiar documentul tradus (contrast, zonă sigură… în engleză).

Previzualizarea, randarea și exportul unei limbi

Parametrul locale funcționează pe GET …/compiled (în interogare), POST …/render (în corp; randarea reține limba), POST …/frame (în corp), GET …/captions.srt și GET …/captions.vtt (în interogare) și POST …/lint (în corp). Limba sursă (sau lipsa lui locale) dă documentul așa cum este; o limbă necunoscută răspunde 404 Locale not found..

curl https://ai.codai.ro/v1/studio/creatives/$ID/render \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "format": "9x16", "quality": "final", "locale": "en" }'

Randările unei limbi se facturează ca orice randare.

Copierea unei limbi

POST /v1/studio/creatives/{id}/locales/{locale}/fork cu { "title"?, "seq"? } creează o creație de sine stătătoare în acea limbă, din versiunea seq (implicit: capul): traducerile sunt scrise chiar în document, suprascrierile sunt eliminate, source devine acea limbă, iar forkOf reține { creativeId, seq, locale }. Titlul implicit este "<titlu> (<locale>)". Răspunde 201 cu creația nouă, plus câmpurile { "locale", "fork_of": { "creative_id", "seq" } }. Gratuit.

SDK-uri și unelte de agent (localizare)

const status = await codai.creatives.locales(c.id);
const units = await codai.creatives.locales(c.id, { locale: 'en', units: true });
const done = await codai.creatives.localize(c.id, { locales: ['en', 'de'], tone: 'informal', glossary: ['codai'] });
const html = await codai.creatives.compiled(c.id, { locale: 'en' });
const job = await codai.creatives.render(c.id, { quality: 'draft', locale: 'en' });
const fork = await codai.creatives.forkLocale(c.id, 'en', { title: 'Teaser de lansare (EN)' });
status = client.creatives.locales(c["id"])
done = client.creatives.localize(c["id"], ["en", "de"], tone="informal", glossary=["codai"])
issues = client.creatives.lint(c["id"], locale="en")
fork = client.creatives.fork_locale(c["id"], "en", title="Teaser de lansare (EN)")

Agenții de pe desktop, telefon și VS Code folosesc creative_localize (creative_id, locales, tone, glossary, voice, only_missing) și creative_locale_fork (creative_id, locale, title); creative_lint primește locale și enumeră codurile de traducere, iar creative_edit acceptă operațiile setLocaleText, setLocale și setLocalization. Ca să umpli câte o creație pentru fiecare rând dintr-un tabel — opțional într-o altă limbă pe fiecare rând — vezi Loturi.

Șabloane

Salvează oricare dintre creațiile tale ca șablon privat, reutilizabil: alegi nodurile text și imagine care se schimbă de la o folosire la alta, iar fiecare devine o variabilă a șablonului. POST /v1/studio/creatives/{id}/template ia creația de la seq (implicit: capul):

curl https://ai.codai.ro/v1/studio/creatives/$CREATIVE_ID/template \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Promo 9:16", "vars": [
        { "node_id": "n_title", "prop": "text", "key": "headline", "label": "Titlu" },
        { "node_id": "n_photo", "prop": "image", "key": "photo" } ] }'
  • prop: "text" pe un nod text: fragmentele lui devin unul singur, cu stilul primului fragment; textul curent devine valoarea implicită.
  • prop: "image" pe un nod image cu un asset literal: id-ul asset-ului curent devine valoarea implicită (asset-ul rămâne în document).
  • key este o literă urmată de cel mult 63 de litere, cifre sau _, unică în șablon; label are implicit numele nodului. Între 1 și 50 de variabile.

Răspunde 201 cu șablonul: { "id", "object": "studio.template", "title", "kind", "source_creative_id", "source_seq", "thumbnail_url", "created_at", "doc" }, unde câmpul vars_schema descrie fiecare variabilă, de exemplu { "headline": { "type": "text", "label", "default", "node_id", "prop" } }. Șablonul își păstrează propria copie a documentului; editările ulterioare ale creației nu îl schimbă. Erori: 400 node_not_found, 400 invalid_pick (tip de nod greșit, deja variabilă sau o imagine fără asset literal), 400 duplicate_key, 404 (creație sau seq necunoscute).

GET /v1/studio/templates?kind=video listează șabloanele tale (cele mai noi primele, fără doc), GET …/{id} citește unul cu documentul lui, DELETE …/{id} îl șterge (creațiile făcute din el nu sunt afectate).

Creare dintr-un șablon — POST /v1/studio/creatives cu template și valorile în vars (după cheie; cheile omise își păstrează valoarea implicită):

curl https://ai.codai.ro/v1/studio/creatives \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "video", "template": "<id șablon>", "vars": { "headline": "Reduceri de toamnă", "photo": "<id asset imagine>" } }'

Versiunea 1 este documentul șablonului cu valorile aplicate; title are implicit titlul șablonului, iar brand_kit_id funcționează în continuare (se aplică după variabile). O valoare text are cel mult 5000 de caractere, o valoare image trebuie să fie id-ul unui asset imagine al tău. Erori: 400 invalid_request (template împreună cu doc sau vars fără template), 400 kind_mismatch, 400 unknown_var, 400 invalid_var_value, 404 (șablon necunoscut; un asset imagine care nu este al tău dă 404 asset_not_found).

const tpl = await codai.creatives.saveTemplate(c.id, {
  title: 'Promo 9:16',
  vars: [{ node_id: 'n_title', prop: 'text', key: 'headline', label: 'Titlu' }],
});
const { data } = await codai.templates.list({ kind: 'video' });
const fromTpl = await codai.creatives.create({ kind: 'video', template: tpl.id, vars: { headline: 'Reduceri de toamnă' } });
await codai.templates.delete(tpl.id);
tpl = client.creatives.save_template(
    c["id"], vars=[{"node_id": "n_title", "prop": "text", "key": "headline", "label": "Titlu"}], title="Promo 9:16"
)
templates = client.templates.list(kind="video")["data"]
from_tpl = client.creatives.create({"kind": "video", "template": tpl["id"], "vars": {"headline": "Reduceri de toamnă"}})
client.templates.delete(tpl["id"])

Preț. Salvarea, listarea, citirea, ștergerea și folosirea șabloanelor sunt gratuite.

Export FCPXML

Predă o creație video unui editor desktop: GET /v1/studio/creatives/{id}/export.fcpxml pune în coadă o arhivă zip cu un proiect FCPXML 1.10 și toate fișierele la care trimite:

  • project.fcpxml — o secvență cu fps-ul (24, 25 sau 30) și dimensiunea documentului; câte un clip pentru fiecare scenă, în ordinea redării, numit după scenă, cu un marcaj care poartă id-ul scenei; vocea, muzica și efectele sonore sunt clipuri audio conectate, pe benzi proprii (voce -1, muzică -2, efecte -3), cu amplificarea lor.
  • media/seg-NNNN.mp4 — segmentele video pe scene, randate la calitate finală, fără sunet.
  • media/audio-* — fișierele de voce, muzică și efecte sonore folosite de cronologie.

Dezarhivează-l și deschide project.fcpxml în Final Cut Pro (File → Import → XML) sau în DaVinci Resolve (File → Import → Timeline); căile către media sunt relative, așa că păstrează dosarul întreg. Premiere Pro îl deschide prin importul FCPXML sau după o conversie (de exemplu cu un convertor din FCPXML în XML); CapCut pentru desktop poate importa fișierele media.

curl "https://ai.codai.ro/v1/studio/creatives/$ID/export.fcpxml?format=16x9" \
  -H "Authorization: Bearer $CODAI_API_KEY"
# 202 { "id": "<id export>", "object": "studio.export", "status": "queued", ... }

curl https://ai.codai.ro/v1/studio/exports/$EXPORT_ID \
  -H "Authorization: Bearer $CODAI_API_KEY"

Implicit, format este primul format al documentului de 1920×1080, 1080×1920 sau 1080×1080, iar seq este versiunea curentă. Corpul răspunsului 202 este un StudioExport cu câmpurile id, object (studio.export), status, creative_id, seq, format, fps, filename, url, expires_at, bytes, rendered_seconds, cost_micro_usd, warnings, error și created_at. Interoghează GET /v1/studio/exports/{id} (răspunde mereu 200 cât timp exportul e al tău) până când status devine succeeded — atunci url este un link de descărcare semnat, valabil o oră (fiecare interogare semnează unul nou) — sau failed, cu error.code și error.message. Asset-urile audio lipsă sau care nu sunt gata sunt sărite și apar în warnings. Erori: 400 unsupported_kind (creația nu e video), 400 unsupported_format, 400 too_long (peste 180 s), 404 (creație sau seq necunoscut), 429 too_many_renders (exporturile se numără împreună cu randările la limita de 3 lucrări în curs).

const queued = await codai.creatives.exportFcpxml(c.id, { format: '16x9' });
const done = await codai.exports.wait(queued.id); // interoghează la 3 s, cel mult 15 min
if (done.status === 'succeeded') console.log(done.filename, done.url, done.expires_at, done.cost_micro_usd);
else console.error(done.error?.code, done.error?.message);
queued = client.creatives.export_fcpxml(c["id"], format="16x9")
done = client.exports.wait(queued["id"])  # interoghează la 3 s, cel mult 15 min
if done["status"] == "succeeded":
    print(done["filename"], done["url"], done["expires_at"], done["cost_micro_usd"])

Preț. Segmentele sunt aceleași pe care le folosesc randările finale: scenele deja randate (de un export anterior sau de o randare finală) se refolosesc gratuit. Se facturează doar secundele randate acum, la prețul randării finale, o singură dată, după ce exportul a reușit (rendered_seconds, cost_micro_usd); un export făcut numai din segmente din cache nu costă nimic. Interogarea este gratuită.

Export Lottie

Folosește o creație video pe web sau într-o aplicație ca animație vectorială ușoară: GET /v1/studio/creatives/{id}/export.lottie o exportă în format Lottie, sincron și gratuit — fără randare, fără coadă.

  • container=json (implicit) — un singur JSON Bodymovin cu imaginile încorporate ca data URL. Rulează în lottie-web, în playerele Lottie pentru Android și iOS și în importatoarele pentru After Effects.
  • container=dotlottie — o arhivă dotLottie v2 (.lottie: manifest.json, a/<format>.json, i/<asset>.<ext>), mai mică, pentru playerele dotLottie.
  • formats=all — toate formatele documentului (dotLottie: câte o animație pe format; JSON: o arhivă zip cu fișierele JSON). Fără el, format alege unul (implicit formatul implicit al documentului); dacă le trimiți pe amândouă primești 400 invalid_request.
  • seq — versiunea (implicit cea curentă).
# fișierul însuși, direct pe disc
curl -OJ "https://ai.codai.ro/v1/studio/creatives/$ID/export.lottie?format=16x9&download=1" \
  -H "Authorization: Bearer $CODAI_API_KEY"

# toate formatele într-o singură arhivă dotLottie, ca obiect export cu URL semnat
curl "https://ai.codai.ro/v1/studio/creatives/$ID/export.lottie?container=dotlottie&formats=all" \
  -H "Authorization: Bearer $CODAI_API_KEY"
# 200 { "id": "<id export>", "object": "studio.export", "kind": "lottie", "container": "dotlottie",
#       "status": "succeeded", "format": "all", "url": "https://…", "cost_micro_usd": "0", "warnings": [...] }

Fără download, răspunsul este un StudioExport (același obiect ca la exportul FCPXML), deja succeeded: url este un link de descărcare semnat, valabil o oră, rendered_seconds este 0, iar cost_micro_usd 0; îl poți citi din nou cu GET /v1/studio/exports/{id}. Cu download=1, răspunsul este fișierul însuși (application/json sau application/zip, content-disposition: attachment) și nu se salvează nimic. Numele fișierelor: <slug>-<format>.lottie.json (JSON), <slug>-<format|all>.lottie (dotLottie), <slug>-lottie.zip (JSON, toate formatele).

Fidelitate. Lottie este un format vectorial, așa că nu tot ce poate arăta un video încape în el. warnings enumeră ce a fost aproximat sau lăsat deoparte:

Se exportăforme (dreptunghi, elipsă, linie), imagini (cover / contain, punct focal, decupare, colțuri rotunjite), text (fonturi de sistem), liste (ca text), subtitrări, grupuri, fundaluri (culoare, gradient liniar, imagine), toate presetările de animație și animațiile cu cadre-cheie, tranzițiile dintre scene, câte un marcaj pe scenă
Se aproximeazăapariția cuvânt cu cuvânt, evidențierea progresivă, stilul subtitrărilor, stilurile de text mixte în același nod, animația opacității unui grup
Nu se exportănodurile video, grafic, bloc HTML, tabel, grilă de celule și audio; imaginile peste bugetul de încorporare de 8 MB

Proveniență. Fiecare fișier spune că a fost făcut cu AI: meta din JSON conține g: "codai Studio", tc: "AI-generated", ai_generated: true și custom: { ai_generated: true, generator: "codai-studio", doc_hash }, iar fiecare răspuns 2xx are antetele x-codai-ai-generated: true și x-codai-generator: codai/codai-studio. Erori: 400 unsupported_kind (creația nu e video), 400 invalid_request, 404 (creație sau seq necunoscut), 413 too_large (fișierul ar depăși 20 MB).

const e = await codai.creatives.exportLottie(c.id, { container: 'dotlottie', formats: 'all' });
console.log(e.filename, e.url, e.warnings);
const file = await codai.creatives.downloadLottie(c.id, { format: '16x9' }); // { bytes, filename, contentType }
e = client.creatives.export_lottie(c["id"], container="dotlottie", formats="all")
print(e["filename"], e["url"], e["warnings"])
f = client.creatives.download_lottie(c["id"], format="16x9")  # {"bytes", "filename", "content_type"}

Preț. Gratuit: fără randare, fără rând de consum, fără plafon de cheltuieli.

Documente (prezentare, document, foaie de calcul)

Documentele Studio — prezentările (PPTX), documentele (DOCX) și foile de calcul (XLSX) — pot fi editate și ele ca o creație. Creația păstrează același conținut ca spec-ul documentului, sub formă de noduri cu tip pe care le editezi prin operații, le previzualizezi live și le generezi înapoi într-un fișier.

Reprezentarea.

  • Prezentare — câte o scenă pe slide (s1, s2, …), cu aranjarea slide-ului în layout (title, section, bullets, two_column, image, chart, table, quote, stats, timeline, closing) și notele pentru vorbitor în notes, pe scenă. Nodurile sunt așezate pe formatul 16:9 (1920×1080) sau 4:3 (1440×1080), cu id-uri precum s3-title, s3-bullets, s3-left-heading sau s3-stat2-value. Listele cu marcatori devin un nod list, tabelele un nod table, graficele un nod chart, cele două coloane câte un group, iar fiecare indicator și fiecare punct din cronologie câte un group.
  • Document — câte o scenă pe pagină (p1, p2, …; o pagină nouă oriunde spec-ul avea un page_break). Fiecare pagină conține un singur group cu rolul flow, ai cărui copii sunt blocurile în ordine (b0, b1, …): titluri, paragrafe, liste, tabele, imagini, grafice și citate.
  • Foaie de calcul — câte o scenă pe foaie (sh1, …, cu numele foii), cu un nod cell_grid (columns, rows, freezeHeader, autofilter, totals, conditional) plus câte un nod chart pentru fiecare grafic, cu celula lui de ancorare în anchor.

Fiecare nod are un rol semantic în role: title, subtitle, heading1–heading4, paragraph, quote, attribution, caption, contact, footer, flow, column-left, column-right, column-heading, stat, stat-value, stat-label, stat-delta, timeline-item, timeline-date, timeline-label și timeline-detail. Generarea citește doar rolurile, tipurile de nod și aranjările scenelor — niciodată id-urile —, așa că poți redenumi, muta și restiliza nodurile liber. Fragmentele de text pot fi code, pot avea un link în link (https) sau o notă de subsol în footnote (DOCX); imaginile primesc alt și caption; paragrafele pot fi aliniate justify. Culorile și fonturile trimit la token-urile temei (color.bg, color.fg, color.accent, font.heading, font.body), iar meta.document păstrează setările documentului (tema, limba, raportul de aspect, subsolul, mărimea paginii, orientarea, antetul, numerotarea paginilor, cuprinsul).

Dintr-un document. POST /v1/studio/documents/{id}/creative (fără corp) convertește unul dintre documentele tale. Este idempotent: primul apel creează creația (201, existing: false) și o leagă de document (documentul raportează apoi creative_id); apelurile următoare întorc aceeași creație (200, existing: true). Corpul răspunsului este creația obișnuită plus document_id și existing. Erori: 404 (document necunoscut), 409 document_spec_missing (nu există spec stocat), 400 invalid_spec, 413 too_large. Gratuit.

curl -X POST https://ai.codai.ro/v1/studio/documents/$DOC_ID/creative \
  -H "Authorization: Bearer $CODAI_API_KEY"
# 201 { "id": "<id creație>", "object": "studio.creative", "kind": "deck", "head_seq": 1, ..., "document_id": "…", "existing": false }

Editare și previzualizare. Folosești outline, find, subtree și operațiile ca la orice creație. GET …/compiled previzualizează acum live și creațiile de tip prezentare, document și foaie de calcul: slide-urile, paginile sau foile apar una sub alta într-o singură pagină HTML (duration_ms: 0, plus kind în răspuns), iar fiecare scenă și fiecare nod pot fi adresate prin id, deci un clic în previzualizare selectează nodul.

Generare. POST /v1/studio/creatives/{id}/build cu { "seq"?, "preview"?, "filename"? } convertește creația (implicit versiunea curentă) înapoi într-un spec de document și generează fișierul PPTX, DOCX sau XLSX pe aceeași cale ca POST /v1/studio/documents. Fiecare generare este o versiune nouă a aceluiași document (based_on = cea mai nouă generare, deci version crește) și reține creative_id și creative_seq. Nodurile pe care un document nu le poate păstra (forme libere, blocuri HTML, videoclipuri, texte suplimentare) sunt sărite și apar în warnings; o scenă adăugată fără layout primește unul dedus din nodurile ei. Erori: 400 not_supported_for_kind (creații video sau imagine), 400 invalid_spec (spec-ul convertit nu trece validarea) și orice eroare de generare a POST /v1/studio/documents.

curl https://ai.codai.ro/v1/studio/creatives/$ID/build \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "preview": true }'
# 200 { "object": "studio.creative_build", "creative_id": "…", "seq": 4, "warnings": [], "document": { "version": 2, "file": { "url": "…" }, ... } }
const c = await codai.creatives.fromDocument(docId); // c.existing spune dacă era deja legată
const { ids } = await codai.creatives.find(c.id, { type: 'list' }); // listele cu marcatori
await codai.creatives.edit(c.id, {
  baseSeq: c.head_seq,
  ops: [{ op: 'updateScene', target: 's2', patch: { notes: 'Începe cu marja din T3.' } }],
});
const built = await codai.creatives.build(c.id, { preview: true });
console.log(built.document.version, built.document.file.url, built.warnings);
c = client.creatives.from_document(doc_id)
client.creatives.edit(
    c["id"],
    base_seq=c["head_seq"],
    ops=[{"op": "updateScene", "target": "s2", "patch": {"notes": "Începe cu marja din T3."}}],
)
built = client.creatives.build(c["id"], preview=True)
print(built["document"]["version"], built["document"]["file"]["url"], built["warnings"])

Preț. Conversia, editarea, previzualizarea (compiled) și generarea sunt gratuite. Costă doar preview: true: previzualizarea documentului se facturează ca POST /v1/studio/documents/{id}/previews (0,002 $ pe pagină, minimum 0,01 $).

Cronologiile devin creații

POST /v1/studio/timelines funcționează ca înainte și acum salvează aceeași cronologie și ca o creație editabilă: corpul 202 are creative_id (sau null dacă copia nu a putut fi făcută — randarea nu este niciodată afectată).

Partajare, revizuire și remix

O creație poate fi partajată printr-un link de vizualizare public, doar pentru citire (oricine are o cheie API o poate remixa în propriul cont, cu resursele copiate), sau printr-un link de revizuire, unde persoane fără cont lasă comentarii ancorate pe un nod și pe un moment, iar aprobatorii aprobă sau cer modificări; o aprobare fixează versiunea. Totul este gratuit — vezi Partajare, revizuire și remix.

Hub (intern)

Hub-ul apelează aceleași rute sub /internal/studio/creatives…, /internal/studio/brand-kits…, /internal/studio/templates… și /internal/studio/exports…, cu un token de serviciu și un user_id; corpurile și erorile sunt identice. Kiturile de brand sunt oglindite complet: POST /internal/studio/brand-kits, PATCH /internal/studio/brand-kits/:id, GET /internal/studio/brand-kits/:id/dtcg, POST /internal/studio/brand-kits/extract (cu url sau logo_asset_id) și POST /internal/studio/creatives/:id/restyle. Sunt oglindite și subtitrările, platformele, corecturile și panoul de ajustare: POST /internal/studio/creatives/:id/captions, GET /internal/studio/creatives/:id/captions.srt și .vtt, POST /internal/studio/creatives/:id/adapt, POST /internal/studio/creatives/:id/lint/fix și GET /internal/studio/creatives/:id/controls. Este oglindită și localizarea: GET /internal/studio/creatives/:id/locales, POST /internal/studio/creatives/:id/localize și POST /internal/studio/creatives/:id/locales/:locale/fork; loturile se află sub /internal/studio/batches… (vezi Loturi); linkurile de partajare, revizuirile și remixul se află sub /internal/studio/creatives/:id/shares…, /internal/studio/creatives/:id/review… și /internal/studio/shares/:token/remix (vezi Partajare, revizuire și remix).

SDK-uri

const c = await codai.creatives.create({ kind: 'video', title: 'Teaser de lansare' });
const r = await codai.creatives.edit(c.id, {
  baseSeq: c.head_seq,
  ops: [{ op: 'setMeta', title: 'Teaser de lansare v2' }],
});
const job = await codai.creatives.render(c.id, { quality: 'draft' });

let frames = await codai.creatives.frame(c.id, { t_ms: [0, 1500] });
while (frames.status === 'running') {
  await new Promise((r) => setTimeout(r, 2000));
  frames = await codai.creatives.frameJob(c.id, frames.frame_job_id);
}
const edit = await codai.creatives.comment(c.id, { t_ms: 1500, comment: 'Fă titlul roșu', base_seq: 2 });
const ab = await codai.creatives.variants(c.id, { base_seq: edit.head_seq, count: 3, focus: ['hook', 'cta'] });
await codai.creatives.pickVariant(c.id, ab.variants[0].creative_id, { base_seq: edit.head_seq });

const kit = await codai.brandKits.extract({ url: 'https://acme.example/' });
const branded = await codai.creatives.create({ kind: 'image', brand_kit_id: kit.id });
c = client.creatives.create({"kind": "video", "title": "Teaser de lansare"})
client.creatives.edit(c["id"], base_seq=c["head_seq"], ops=[{"op": "setMeta", "title": "v2"}])
job = client.creatives.render(c["id"], {"quality": "draft"})
frames = client.creatives.frame(c["id"], t_ms=[0, 1500], exact=True)
if frames["status"] == "running":
    frames = client.creatives.frame_job(c["id"], frames["frame_job_id"])  # interoghează până la "succeeded"
edit = client.creatives.comment(c["id"], t_ms=1500, comment="Fă titlul roșu", base_seq=2)
ab = client.creatives.variants(c["id"], base_seq=edit["head_seq"], count=3, focus=["hook", "cta"])
client.creatives.pick_variant(c["id"], ab["variants"][0]["creative_id"], base_seq=edit["head_seq"])

Pe această pagină