Creații
Documente creative editabile în Kodwen Studio — creezi un video, o imagine sau o prezentare ca JSON, le editezi prin modificări mici, versionate, 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, foaie de calcul sau audio — melodii și podcasturi, vezi Creații audio), 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ă | Cale | Rezultat |
|---|---|---|
POST | /v1/studio/creatives | Creaț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}/outline | Schiță text compactă, formate și variabile |
POST | /v1/studio/creatives/{id}/find | Id-urile nodurilor care se potrivesc |
POST | /v1/studio/creatives/{id}/subtree | JSON-ul complet al unor noduri sau al unei scene |
POST | /v1/studio/creatives/{id}/ops | Aplică un lot de operații → versiune nouă |
GET | /v1/studio/creatives/{id}/versions | Istoricul versiunilor |
POST | /v1/studio/creatives/{id}/revert | Versiune curentă nouă cu documentul unei versiuni vechi |
GET | /v1/studio/creatives/{id}/branches | Ramuri cu nume (main prima) |
POST | /v1/studio/creatives/{id}/branches | Ramură nouă dintr-o versiune, 201 |
DELETE | /v1/studio/creatives/{id}/branches/{name} | Șterge o ramură și versiunile ei |
POST | /v1/studio/creatives/{id}/filmstrip | O miniatură pe clip (compozitorul comun, cota de cadre) |
GET | /v1/studio/jobs | Lucrările Studio în curs și recente (coada) |
POST | /v1/studio/creatives/{id}/fork | Creație nouă dintr-o versiune (201) |
POST | /v1/studio/creatives/{id}/lint | Contrast, 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}/render | Randare MP4 (202, codai-render; 200 când randarea identică există deja) |
POST | /v1/studio/creatives/{id}/frame | Cadre 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}/comments | Transformă un comentariu pe un cadru într-o editare (facturat pe tokeni) |
POST | /v1/studio/creatives/{id}/variants | Generează variante A/B ca derivate + randări draft (facturat pe tokeni) |
GET | /v1/studio/creatives/{id}/variants | Ultimul grup de variante, cu stări și starea randării |
POST | /v1/studio/creatives/{id}/variants/{variantId}/pick | Aplică varianta câștigătoare pe original |
DELETE | /v1/studio/creatives/{id} | Ștergere |
POST | /v1/studio/brand-kits | Creează un kit de brand din tokeni sau dintr-un fișier DTCG (201) |
POST | /v1/studio/brand-kits/extract | Kit 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}/dtcg | Descarcă 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}/restyle | Reaplică un kit de brand pe o creație → versiune nouă |
POST | /v1/studio/creatives/{id}/captions | Subtitră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}/adapt | Adaptează la preseturi de platformă (dimensiuni + zone sigure) → versiune nouă |
POST | /v1/studio/creatives/{id}/lint/fix | Corectează automat problemele lint (gratuit; tură de agent plătită, opțională) |
GET | /v1/studio/creatives/{id}/controls | Modelul 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}/localize | Traducere în cel mult 10 limbi → versiune nouă (facturată pe tokeni) |
POST | /v1/studio/creatives/{id}/locales/{locale}/fork | Creație de sine stătătoare într-o singură limbă (201) |
POST | /v1/studio/creatives/{id}/template | Salvează 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}/creative | Editează 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}/build | Generează dintr-o creație o versiune nouă PPTX / DOCX / XLSX |
POST | /v1/studio/creatives/{id}/shares | Creează un link de vizualizare sau de revizuire (201, tokenul apare o singură dată) — vezi Partajare, revizuire și remix |
GET | /v1/studio/creatives/{id}/shares | Linkurile de partajare active |
DELETE | /v1/studio/creatives/{id}/shares/{shareId} | Revocă un link de partajare (204) |
GET | /v1/studio/creatives/{id}/review | Starea revizuirii și versiunea aprobată |
POST | /v1/studio/creatives/{id}/review | Cere 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/threads | Fir nou de comentarii ancorat (201) |
POST | /v1/studio/creatives/{id}/review/threads/{threadId}/replies | Răspunde într-un fir (201) |
POST | /v1/studio/creatives/{id}/review/threads/{threadId}/resolve | Rezolvă un fir |
POST | /v1/studio/creatives/{id}/review/threads/{threadId}/reopen | Redeschide un fir |
DELETE | /v1/studio/creatives/{id}/review/comments/{commentId} | Șterge un comentariu de revizuire (204) |
POST | /v1/studio/shares/{token}/remix | Copiază 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șihtml_block, fiecare cu unid, unlayoutși, opțional,timing,anim(preseturi precumfade-in,slide-up,ken-burns,zoom-in,typewritersau cadre cheie — vezi Mișcare) și suprascrieri per format (overrides). - Fișierele media se referă la asset-urile tale prin id.
brand.tokensșivarsse 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" }'Piste (schemaVersion 2)
Documentele video folosesc piste reale: schemaVersion: 2 adaugă tracks — piste
vizuale (video, overlay, text, captions) cu clipuri așezate la milisecunde absolute. Un clip este un clip
media (type: "video", cu propriul interval in/out), un singur nod (type: "node") sau un clip compus
(type: "compound", o scenă veche cu nodurile ei). scenes devin secțiuni cu nume, fără noduri. Migrarea face din
fiecare scenă un clip compus cu același id, așa că comentariile, recenziile și cache-urile ancorate pe id-ul unei
scene funcționează în continuare, iar randările rămân identice byte cu byte. Orice document salvat este versiunea 2 (un document versiunea 1
trimis de tine e migrat la salvare); schița listează tracks → clips, ancorele acceptă clip_id (cu scene_id ca alias), iar
operațiile de pistă/clip (addVisualTrack, insertClip, moveVisualClip, reorderVisualClip, trimVisualClip, splitVisualClip,
setClipProps, setClipAnim, flattenClip, …) le editează. reorderVisualClip { target, after } mută un clip mai
devreme sau mai târziu pe pista lui și reașază începuturile (tranzițiile își păstrează suprapunerea), cum făcea mutarea
unei scene. Segmentele de randare urmează grupurile de clipuri
care se suprapun pe toate pistele.
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.
Mișcare
setAnim înlocuiește lista anim a unui nod; fiecare intrare este un preset sau o animație cu cadre cheie. O intrare de preset este { "preset", "atMs"?, "durMs"?, "ease"?, "params"? }; una cu cadre cheie este { "prop", "keyframes": [{ "tMs", "value", "ease"? }] }, unde prop este unul dintre opacity, x, y, scale și rotation.
{
"op": "setAnim",
"target": "bg",
"anim": [{ "preset": "zoom-in", "params": { "scale": 1.2 }, "ease": "in-out-cubic" }]
}| Preset | Ce face | Durată implicită |
|---|---|---|
fade-in, fade-out | Apare treptat; dispare treptat la sfârșitul nodului | 400 ms |
slide-up, slide-down, slide-left, slide-right | Intră glisând pe params.distance (implicit 48 px), cu apariție treptată | 500 ms |
scale-pop | Apare mărindu-se de la 0,6, cu o ușoară depășire | 450 ms |
typewriter | Dezvăluie textul caracter cu caracter | 1200 ms |
word-stagger | Cuvintele apar unul după altul (params.wordDurMs, implicit 350) | 600 ms |
count-up | Numără de la params.from (implicit 0) la params.to (implicit: numărul din text) | 1200 ms |
wipe | Dezvăluie nodul printr-o ștergere orizontală | 600 ms |
highlight-sweep | Trece culoarea de evidențiere peste text | 600 ms |
ken-burns | Zoom lent, liniar, spre punctul focal (params.scale, implicit 1,08) | Tot nodul |
zoom-in, zoom-out | Apropiere / depărtare a camerei pe media care umple cadrul, în jurul punctului focal (params.scale, implicit 1,14) | Tot nodul |
pan-left, pan-right | Panoramare a camerei peste media ușor mărită (params.scale, implicit 1,12) | Tot nodul |
rise | Intrare: urcă (params.distance, implicit 64 px) și se mărește de la 0,96, cu apariție treptată | 700 ms |
scale-out | Ieșire: se micșorează la 0,92 și dispare treptat, așezată la sfârșitul nodului, ca fade-out | 400 ms |
Curbe de animație. ease, pe un cadru cheie sau pe un preset, este unul dintre cele cinci cuvinte-cheie CSS (linear, ease, ease-in, ease-out, ease-in-out) sau una dintre curbele out-cubic, out-quart, out-expo, in-cubic, in-out-cubic, in-out-quart, in-out-sine, out-back și in-out-back. Pe un preset înlocuiește curba principală a presetului (typewriter îl ignoră).
Corecție de culoare. Nodurile imagine și video acceptă grade în proprietăți (setProps): { brightness?, contrast?, saturate?, blur? }. Pentru brightness și contrast (0–2) și saturate (0–3), valoarea 1 lasă media neschimbată; blur este între 0 și 200 de pixeli ai documentului. Corecția se aplică în previzualizarea web, în randările MP4 și în compozitor, dar nu și în exportul Lottie (avertismentul grade_not_exported).
{ "op": "setProps", "target": "bg", "props": { "grade": { "contrast": 1.1, "saturate": 0.9 } } }Tranziții între scene. transitionIn al unei scene (updateScene) este { "type", "durMs" }: type este unul dintre cut, fade, slide-left, slide-right, slide-up, slide-down, wipe, zoom și zoom-out, iar durMs este cel mult 5000.
{
"op": "updateScene",
"target": "intro",
"patch": { "transitionIn": { "type": "slide-right", "durMs": 500 } }
}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.
Ramuri. main este linia implicită. POST …/branches {name, from?, seq?} pornește o ramură din orice versiune;
versiunile ei continuă de la acel seq (istoricul de dedesubt este comun). Trimite branch (în corp) sau ?branch=
(în interogare) la GET {id}, /ops, /versions, /revert, /fork, /frame, /filmstrip și /comments;
răspunsurile numesc branch. Numele: a-z, 0-9, cratime, cel mult 40 de caractere, cel mult 20 de ramuri pe creație.
Erori: branch_not_found (404), branch_exists, too_many_branches, branch_protected (main) și
branch_has_children (409). SDK: creatives.branches, createBranch, deleteBranch (Python branches,
create_branch, delete_branch).
Filmstrip și coadă. POST …/filmstrip desenează câte o miniatură la mijlocul fiecărui clip cu compozitorul comun
(același cod ca previzualizarea din editor), pe calea lucrărilor de cadre: din cache, gratuit pentru primele 60 de
lucrări de cadre pe zi, apoi la minimul de ciornă. GET /v1/studio/jobs listează lucrările tale în curs și din ultimele
15 minute, de orice tip (randări, cadre, exporturi, producții, operații media, Music Room, încărcări), cu progres și
timp estimat; este gratuit.
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âmp | Semnificație |
|---|---|
t_ms | 1–6 momente în milisecunde, fiecare sub durata totală a creației (obligatoriu) |
format | Un formats[].id (implicit formatul implicit al documentului) |
exact | false (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 |
version | Versiunea 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âmp | Semnificație |
|---|---|
t_ms | Momentul cursorului la comentariu (obligatoriu) |
comment | Ce trebuie schimbat, 1–2000 de caractere (obligatoriu) |
base_seq | head_seq-ul citit ultima dată (obligatoriu; o bază veche e rebazată ca la /ops) |
node_id / scene_id / clip_id | Nodul, scena sau clipul selectat (implicit: scena — la videoclipurile schemaVersion 2 clipul primei piste video — care rulează la t_ms; scene_id e alias pentru clip_id) |
apply | true (implicit) scrie o versiune; false întoarce doar ops propuse și diff-ul lor |
chips | Cel mult 8 chip-uri de context în care trebuie să rămână editarea: { "kind": "clip" | "node", "id" }, { "kind": "range", "start_ms", "end_ms" } sau o zonă din scenă { "kind": "area", "t_ms", "x", "y", "w", "h", "format"? } (px ai compoziției) |
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 (citește din nou documentul, apoi 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.
Diferența op-urilor (SX-07). Cu apply: false răspunsul conține și diff: { scoped, entries }: câte o intrare pentru fiecare op propus, cu action (insert, remove, move, retime, set), targets (id + o etichetă scurtă label), fields, textul before / after, in_scope (în interiorul chip-urilor) și depends_on (op-urile anterioare de care are nevoie). Editoarele Studio bifează implicit op-urile din selecție, te lasă să accepți sau să respingi fiecare op și trimit doar ce ai acceptat la POST …/ops, cu head_seq-ul propunerii ca base_seq.
Preț. Facturat pe tokeni ca orice apel text kodwen (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âmp | Semnificație |
|---|---|
base_seq | Versiunea din care se derivă (obligatoriu) |
count | 1–5 variante (implicit 3) |
focus | Oricare dintre hook (deschiderea), title, cta, visual (culori, așezare, imagini, mișcare), copy (implicit hook, title, cta) |
brief | Indicații opționale, cel mult 1000 de caractere |
render | true (implicit, doar video) pune în coadă o randare draft pentru fiecare variantă |
format | Id-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 kodwen (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:
$valueal unei culori poate fi și un șir"#abc"/"#aabbcc",{ "hex" }sau{ "colorSpace": "srgb", "components" };$typepoate sta pe token sau pe grupul lui;$valueal unui font poate fi un singur șir. Kiturile salvate folosesc mereu forma canonică de mai sus. $valueal unui font enumeră 1–4 nume de familii (o listă goală înseamnă necunoscut);radiusare 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 înerror.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.
Extragere dintr-o pagină web sau dintr-un logo
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/creativescu"brand_kit_id": "<id kit>": versiunea 1 are dejabrand = { kitId, tokens }și logoul înregistrat înassets(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 unsetAssetpentru logo) laPOST …/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âmp | Semnificație |
|---|---|
brand_kit_id / tokens | Exact unul: unul dintre kiturile tale sau o hartă plată de tokeni |
base_seq | Versiunea citită (implicit: capul); o bază veche este rebazată ca la /ops |
snap | true mută orice altă culoare literală pe cea mai apropiată culoare din kit (implicit false) |
fonts | false lasă fonturile neatinse (implicit true) |
force | true 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:
- Transformare în tokeni. Brandul nou este vechiul
brand.tokenscu tokenii kitului deasupra. Fiecare#rrggbbliteral 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ă. - 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. - 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
headingFontpentru titluri (roluriletitle,subtitle,heading1–heading4,column-heading,stat-valuesau text de mărime 48 și peste în afara documentelor) șibodyFontîn rest. - Nodurile blocate (sau cele aflate într-un grup blocat) sunt sărite cu avertismentul
restyle skipped locked node <id>, în afară de cazulforce: 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 kodwen (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âmp | Semnificație |
|---|---|
clip_id | Clipul audio de subtitrat (implicit: primul clip de voce) |
preset | bold-pill (implicit), clean-outline, karaoke-pop sau minimal |
node_id | Id-ul nodului de subtitrare (implicit captions, apoi captions-2…); un nod de subtitrare existent cu acest id este înlocuit |
script | Textul exact al narațiunii, cel mult 20 000 de caractere (implicit: textul din care a fost generat asset-ul de voce) |
language | Limba transcrierii (implicit: locale al documentului) |
retranscribe | true ignoră cuvintele din cache și transcrie din nou |
base_seq / force | Ca 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:
| Preset | Stil | Mărimea textului | Cuvinte pe rând | Culori |
|---|---|---|---|---|
bold-pill | Fundal rotunjit sub text | 5,5 % | 4 | Alb |
clean-outline | Text cu contur | 5 % | 6 | Alb |
karaoke-pop | Evidențiere cuvânt cu cuvânt | 6 % | 3 | Alb, evidențiere cu tokenul accent (sau #ffd60a) |
minimal | Text cu contur | 4 % | 8 | Alb |
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ă | Dimensiune | Zonă sigură (sus / dreapta / jos / stânga) | Text minim |
|---|---|---|---|---|
tiktok | TikTok | 1080×1920 | 8 % / 13 % / 20 % / 5 % | 36 px |
reels | Instagram Reels | 1080×1920 | 7 % / 10 % / 18 % / 5 % | 36 px |
shorts | YouTube Shorts | 1080×1920 | 7 % / 12 % / 17 % / 5 % | 36 px |
feed-square | Feed 1:1 | 1080×1080 | 5 % pe fiecare latură | 32 px |
feed-portrait | Feed 4:5 | 1080×1350 | 5 % pe fiecare latură | 32 px |
youtube | YouTube 16:9 | 1920×1080 | 5 % pe fiecare latură | 28 px |
Zonele sunt aproximări intenționat prudente ale ghidurilor publicate de platforme, care se schimbă des.
| Câmp | Semnificație |
|---|---|
platforms | 1–6 id-uri de platformă distincte (obligatoriu) |
focal | none (implicit) sau vision — vezi mai jos |
base_seq / force | Ca 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ă:
- 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ă înmeta.platforms[<id format>]. - 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). - Textul sub minimul platformei primește o mărime mai mare doar în acel format.
- 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 kodwen (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.
| Cod | Severitate | Corectabil | Ce înseamnă |
|---|---|---|---|
low_contrast | eroare | nu | Contrastul textului sub pragul WCAG (4,5:1 sau 3:1 pentru text mare) |
outside_safe_area | avertisment | nu | Text sau subtitrări în afara zonei sigure (zona platformei când formatul implicit este legat de o platformă) |
empty_text | avertisment | nu | Un nod text fără text |
captions_no_words | eroare | nu | Un nod de subtitrare fără cuvinte |
captions_no_clip | avertisment | nu | Subtitrări care trimit la un clip audio lipsă |
missing_asset | eroare | nu | Un id de asset neînregistrat în document |
scene_zero_duration | eroare / avertisment | nu | O scenă de 0 ms (eroare) sau un nod care nu apare niciodată (avertisment) |
too_long | eroare | nu | Durata totală depășește 180 s |
chart_no_svg | avertisment | nu | Un grafic fără SVG randat în prealabil |
unresolved_ref | avertisment | nu | O referință la o variabilă sau la un token necunoscut |
empty_list | avertisment | nu | O listă fără text în elemente |
off_brand_color | avertisment | da | O culoare literală din afara paletei de brand (nuanțele neutre de gri nu sunt semnalate niciodată) |
outside_platform_safe_zone | avertisment | da | Text sub interfața unei platforme, într-un format legat de acea platformă |
text_too_small | avertisment | da | Text sub pragul de lizibilitate al unui format (value = px) |
missing_alt | avertisment | nu | O imagine dintr-o prezentare sau dintr-un document fără text alternativ |
missing_captions | avertisment | nu | Un clip de voce fără nod de subtitrare — folosește Subtitrări |
reading_speed | avertisment | nu | Un rând de subtitrare mai rapid de 20 de caractere pe secundă (value = caractere pe secundă) |
flashing | eroare / avertisment | da / nu | O 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) |
motion_off_frame | avertisment | nu | Un text, o subtitrare sau o listă al cărei conținut stă parțial în afara cadrului ori își termină mișcarea tăiat de marginea cadrului (value = px în afară) |
motion_overlap | avertisment | nu | Două texte din aceeași scenă, pe ecran împreună mai mult de 0,5 s, ale căror casete se suprapun cu peste 15 % din cea mai mică (value = procent) |
text_reading_time | avertisment | nu | Un text sau o listă de cel puțin 3 cuvinte care poate fi citit(ă) mai puțin de 0,5 s + 1 s la 4 cuvinte (value = ms afișate) |
motion_repeat | avertisment | nu | Textul principal intră cu aceeași animație în cel puțin 4 scene la rând sau imaginea de fundal folosește aceeași mișcare de cameră în cel puțin 2 scene la rând (value = lungimea seriei) |
narration_overrun | avertisment | nu | Un clip de voce care se termină la peste 50 ms după sfârșitul scenei în care începe (value = ms depășiți) |
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âmp | Semnificație |
|---|---|
codes | Corectează doar aceste coduri (implicit: toate codurile corectabile) |
agent | true adaugă o tură plătită de agent pentru problemele rămase (implicit false) |
base_seq / force | Ca 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
kodwen (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 control | Se potrivește variabilelor de tip | Câmpuri suplimentare |
|---|---|---|
text | text, number | multiline |
color | color | — |
slider | number | min și max (obligatorii, min < max), step |
toggle | boolean | — |
select | text, number, color | options (obligatoriu, 1–24 elemente { "value", "label"? }) |
image | image | — |
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": ["Kodwen", "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 }] }
}
}
}
}
}sourceeste limba documentului însuși (implicit:localeal documentului, altfelen). Limbile sunt etichete BCP-47 (ro,en,de-DE,pt-BR), normalizate la intrare (ro_ro→ro-RO).textsasociază cheii unei unități de traducere traducerea eit, amprentaha textului sursă din care a fost făcută șiby(agentsauuser). 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ă, iarcaptionsî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 unitate | Cheie | Unde |
|---|---|---|
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>:caption | Antetele, 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>.name | Etichetele graficului |
alt, caption | <id nod>:alt, <id nod>:caption | Textul 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>:words | Cuvintele subtitrării, unite prin spații |
notes | scene:<id scenă>:notes | Notele vorbitorului |
title | doc:title | Titlul creației |
var | var:<cheie> | O variabilă text |
narration | clip:<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": ["Kodwen"],
"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": ["Kodwen", "Black Friday"] }'| Câmp | Semnificație |
|---|---|
locales | 1–10 limbi țintă distincte, niciodată limba sursă (obligatoriu) |
tone | formal (adresare politicoasă, de exemplu dumneavoastră, Sie) sau informal (tu, du); salvat pentru fiecare limbă |
glossary | Cel mult 100 de termeni (fiecare de cel mult 100 de caractere) păstrați exact cum sunt scriși; salvat în document |
only_missing | true (implicit) traduce doar unitățile fără o traducere actuală; false traduce totul din nou |
voice | true (doar video) reînregistrează și narațiunea și resincronizează subtitrările pentru fiecare limbă — vezi mai jos |
base_seq / force | Ca 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 kodwen (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 } }setLocaleTextsetează 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ă.byare implicit valoareauser. O cheie necunoscută dănot_found, o unitate a unui nod blocatlocked(fărăforce), iar limba sursăinvalid.setLocaleseteazătoneal unei limbi,audio(id clip → id asset; asset-ul trebuie înregistrat cusetAsset) șicaptions(id nod de subtitrare → cuvinte);"remove": trueșterge limba.setLocalizationsetează limba sursăsourcea documentului, glosarulglossaryși legăturaforkOf;"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/fixsetează pe acel nodautoFit: "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: ['Kodwen'] });
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=["Kodwen"])
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 nodtext: fragmentele lui devin unul singur, cu stilul primului fragment; textul curent devine valoarea implicită.prop: "image"pe un nodimagecu un asset literal: id-ul asset-ului curent devine valoarea implicită (asset-ul rămâne în document).keyeste o literă urmată de cel mult 63 de litere, cifre sau_, unică în șablon;labelare 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.project.xml— aceeași cronologie ca Final Cut Pro 7 XML (xmeml v5) pentru Adobe Premiere Pro: o pistă video cu bucățile de scenă și marcajele lor, piste audio pe roluri (dialog, muzică, efecte), cu amplificarea lor.project.edl— lista de tăieturi video ca EDL CMX 3600 (timecode non-drop de la00:00:00:00, câte un eveniment pe bucată de scenă), pentru orice editor; sunetul rămâne în XML / FCPXML.captions.srt— subtitrările creației în format SubRip, pe aceeași cronologie, doar când creația are subtitrări.credits.txt— mențiunile pentru materialele de stoc, doar când sunt folosite.
Dezarhivează-l și păstrează dosarul întreg (căile către media sunt relative). Deschide project.fcpxml în Final Cut
Pro (File → Import → XML) sau în DaVinci Resolve (File → Import → Timeline); în Premiere Pro folosește File →
Import și alege project.xml; orice alt editor importă project.edl, apoi adaugă captions.srt ca pistă de
subtitrări. CapCut pentru desktop poate importa fișierele media. Răspunsul StudioExport listează fișierele de proiect
în files.
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,formatalege unul (implicit formatul implicit al documentului); dacă le trimiți pe amândouă primești400 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; corecția de culoare (grade) a unei imagini (avertismentul grade_not_exported) |
Proveniență. Fiecare fișier spune că a fost făcut cu AI: meta din JSON conține g: "Kodwen 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 înlayout(title,section,bullets,two_column,image,chart,table,quote,stats,timeline,closing) și notele pentru vorbitor înnotes, pe scenă. Nodurile sunt așezate pe formatul 16:9 (1920×1080) sau 4:3 (1440×1080), cu id-uri precums3-title,s3-bullets,s3-left-headingsaus3-stat2-value. Listele cu marcatori devin un nodlist, tabelele un nodtable, graficele un nodchart, cele două coloane câte ungroup, iar fiecare indicator și fiecare punct din cronologie câte ungroup. - Document — câte o scenă pe pagină (
p1,p2, …; o pagină nouă oriunde spec-ul avea unpage_break). Fiecare pagină conține un singurgroupcu rolulflow, 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 nodcell_grid(columns,rows,freezeHeader,autofilter,totals,conditional) plus câte un nodchartpentru fiecare grafic, cu celula lui de ancorare înanchor.
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 $).
Producții (brief → rezultat gata)
POST /v1/studio/productions transformă un brief într-un rezultat gata, pe server. kind alege ce se construiește (implicit video): un singur apel de plan limitat (codai::production-storyboard), imaginile sau muzica necesare sub o singură rezervare din portofel, o creație editabilă, apoi rezultatul din generatoarele obișnuite. Interoghezi GET /v1/studio/productions/{id} până când status devine succeeded; producțiile non-video întorc kind, output_asset_id și outputs (active Studio cu un role, url semnat pentru 1 h). starter, voice, narration, quality, music, aspect_ratio și duration_s se aplică doar la video (duration_s, 15–60, este durata țintă în secunde: storyboard-ul și vocea sunt gândite pentru ea, iar scenele se lungesc până o ating; fără ea un video durează circa 15–30 s); audio folosește Lyria 3 Clip și nu este disponibil pentru cheile cu rezidența datelor în UE. Bugetul (implicit 3 $, maxim 20 $) plafonează toată producția.
kind | Ce conține planul | Rezultat (outputs[].role → format) | C2PA |
|---|---|---|---|
video | Storyboard pe un șablon (texte, o imagine, o replică de voce, o mișcare de cameră și o tranziție pe scenă) | Randare MP4 (render_id) la calitatea cerută în quality, verificare vizuală + o reparare | Da |
image | Titlu, subtitlu, prompt de imagine, format (1:1, 4:5 sau 9:16) | image → PNG | Da |
deck | Titlu + 4–8 slide-uri (marcatori, imagine opțională), 16:9 | document → PPTX | Nu (marcaj în proprietățile documentului) |
doc | Titlu + 4–8 secțiuni (paragraf, marcatori, imagine opțională), A4 | document → DOCX | Nu (marcaj în proprietățile documentului) |
sheet | Titlu + coloane + 5–20 rânduri, o singură foaie | document → XLSX | Nu (marcaj în proprietățile documentului) |
carousel | 4–8 slide-uri (titlu, text, imagine opțională), 4:5 | pages → ZIP cu PNG-uri, cover → PNG | Coperta da; ZIP-ul nu (XMP în fiecare PNG) |
print | Afiș sau flyer: titlu, text, prompt de imagine, A4 sau A5 | pdf → PDF | Nu (marcaj în PDF Info) |
audio | Jingle sau cântec: titlu, gen, prompt, versuri opționale, ≤ 60 s | audio → MP3 | Da |
Video (kind: "video")
Apelul de storyboard scrie textele, câte un prompt de imagine și câte o replică de voce pe scenă. Tot el alege, pentru fiecare scenă, o mișcare de cameră pentru imaginea de fundal (zoom-in, zoom-out, pan-left, pan-right sau static) și tranziția de intrare în scenă (orice tip de tranziție între scene). Fiecare scenă își primește imaginea și replica, sub voce se adaugă un fundal muzical, rezultatul se salvează ca o creație editabilă și se pune în coadă o randare. Când randarea e gata, verificarea vizuală poate corecta textul o dată; randarea de reparare folosește același quality.
| Câmp | Semnificație |
|---|---|
quality | final (implicit): rezoluție completă la 30 fps. draft: 540p la 15 fps, la jumătate din prețul randării |
music | true (implicit): adaugă un fundal muzical instrumental (codai-music), generat dintr-un prompt de muzică scris de storyboard și așezat sub voce, la un nivel mai scăzut. false: fără muzică |
Fundalul muzical este omis, cu o intrare în warnings, acolo unde muzica nu e disponibilă (gateway-urile cu rezidența datelor în UE; cheile care nu au voie să folosească codai-music) sau când generarea lui eșuează; producția reușește oricum. Se facturează la prețul obișnuit codai-music, în limita bugetului producției. Răspunsul unei producții video conține quality și music (true când s-a adăugat efectiv un fundal muzical).
curl https://ai.codai.ro/v1/studio/productions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Idempotency-Key: promo-001" \
-H "Content-Type: application/json" \
-d '{"brief":"Promo de 20 s pentru reducerea de toamnă: 30% la toată cafeaua boabe, comandă online","language":"ro",
"aspect_ratio":"9:16","quality":"final","music":true}'
# 202 { "id": "…", "object": "studio.production", "status": "running", "phase": "render",
# "quality": "final", "music": true, "warnings": [], ... }SDK-uri: productions.create({ brief, quality: 'draft', music: false }) (Python productions.create(brief, quality="draft", music=False)); unealta de agent creative_produce (quality, music).
Verificare înainte de randarea finală (review)
review: true adaugă o oprire înainte de randarea finală. Producția scrie storyboard-ul, face imaginile, vocea și muzica, randează o previzualizare și apoi așteaptă în etapa review (status rămâne running). Fără review nu se schimbă nimic: producția randează imediat, ca până acum.
curl https://ai.codai.ro/v1/studio/productions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Idempotency-Key: promo-002" \
-H "Content-Type: application/json" \
-d '{"brief":"Promo de 20 s pentru reducerea de toamnă: 30% la toată cafeaua boabe, comandă online","language":"ro",
"aspect_ratio":"9:16","review":true}'
# 202 { "id": "…", "status": "running", "phase": "assets",
# "review": { "state": "preparing", "animatic_render_id": null, "stills": [], "since": null, "expires_at": null }, ... }Interoghezi GET /v1/studio/productions/{id} până când phase devine review. Obiectul review conține atunci ce este de verificat:
| Câmp | Semnificație |
|---|---|
state | preparing cât timp se fac imaginile și previzualizarea, waiting cât așteaptă răspunsul tău, apoi approved, cancelled sau expired |
animatic_render_id | Previzualizarea, o randare obișnuită: o citești cu GET /v1/studio/renders/{id} pentru MP4 și imaginea de copertă. null când previzualizarea a eșuat; producția așteaptă oricum, doar cu imaginile |
stills | Câte o intrare pe scenă: scene, asset_id (imaginea generată, null când a eșuat) și narration (replica de voce) |
since, expires_at | Când a început așteptarea și când expiră (după 7 zile) |
Cât timp producția așteaptă poți edita creația ei (creative_id) ca pe oricare alta; modificările sunt randate când aprobi. Apoi dai răspunsul:
curl https://ai.codai.ro/v1/studio/productions/<id>/review \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"decision":"approve"}'
# 200 { "id": "…", "status": "running", "phase": "render", "review": { "state": "approved", ... }, ... }approvepornește randarea finală la calitatea cerută înquality, facturată ca orice randare. Verificarea de după randare rulează ca de obicei.cancelîncheie producția custatus: "failed"șierror: "cancelled". Creația rămâne în biblioteca ta.- Dacă nu vine niciun răspuns în 7 zile, producția se încheie la fel, cu
error: "gate_expired". 402 insufficient_fundslaapproveînseamnă că portofelul nu acoperă randarea finală. Producția rămâne în așteptare, deci poți alimenta portofelul și aproba din nou.409 not_in_reviewînseamnă că producția nu așteaptă un răspuns. Dacă trimiți din nou aceeași decizie după ce a fost aplicată, primești200.
Costuri: cât timp o producție așteaptă nu se rezervă și nu se facturează nimic. Imaginile, vocea și muzica se facturează când sunt făcute, ca la orice producție, iar previzualizarea se facturează ca o randare de probă (draft). render_id și first_render_id nu indică niciodată previzualizarea.
SDK-uri: productions.create({ brief, review: true }), apoi productions.review(id, { decision: 'approve' }) (Python productions.create(brief, review=True), apoi productions.review(id, decision="approve")). productions.wait() și productions.run() se întorc când producția ajunge să aștepte răspunsul. Unealta de agent creative_produce: review pornește o producție cu verificare, iar production_id împreună cu decision dă răspunsul.
Compoziție liberă (director)
director alege cine așază videoclipul în pagină. starter (implicit) este comportamentul descris mai sus: modelul completează unul dintre șabloanele video incluse. Cu free modelul scrie singur toată compoziția: lista de cadre, locul fiecărui text pe grilă, mărimile de literă și intrarea fiecărui text, pe același sistem de design pentru mișcare ca șabloanele. Rezultatul este o creație editabilă ca oricare alta. free nu se poate combina cu starter (id-ul unui șablon): răspunsul este 400. Răspunsul conține director.
curl https://ai.codai.ro/v1/studio/productions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"brief":"Promo de 20 s pentru reducerea de toamnă: 30% la toată cafeaua boabe, comandă online","language":"ro",
"director":"free"}'
# 202 { "id": "…", "status": "running", "director": "free", "starter": null, ... }Runde de critică (critique_rounds)
Fără acest câmp, verificarea de după randare este un singur apel vizual pe cadre fixe, care poate corecta textul o dată. critique_rounds: 1 sau 2 o înlocuiește cu o buclă de critică de cel mult atâtea runde: din randare se iau mostre (cadre fixe plus cadre de început, în care se vede mișcarea), o critică o notează după o listă scurtă de criterii scrise pentru brief, iar schimbările ei (text, așezare, sincronizare, animație) sunt aplicate și randate din nou. Fiecare rundă poate adăuga la cost o randare și un apel de model, în limita bugetului producției.
curl https://ai.codai.ro/v1/studio/productions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"brief":"Promo de 20 s pentru reducerea de toamnă: 30% la toată cafeaua boabe, comandă online","language":"ro",
"critique_rounds":2}'
# mai târziu, GET /v1/studio/productions/<id>:
# { "status": "succeeded", "phase": "ready",
# "critique": { "max_rounds": 2, "rounds": [
# { "round": 1, "verdict": "revise", "score": 62, "issues": ["…"], "ops": 5, "reverted": false },
# { "round": 2, "verdict": "accept", "score": 81, "issues": [], "ops": 0, "reverted": false } ] }, ... }score merge de la 0 la 100, iar ops numără schimbările aplicate în runda respectivă. reverted: true înseamnă că schimbările rundei anterioare au înrăutățit videoclipul: au fost anulate și se livrează randarea de dinainte. qa își păstrează forma obișnuită (issues din ultima rundă, fixes ca număr total de schimbări aplicate). Scorurile sunt părerea criticii despre această randare; nu promit cum se va comporta videoclipul la public.
Toate cele trei opțiuni se aplică doar la kind: "video" (400 cu alt tip) și se pot combina. SDK-uri: productions.create({ brief, director: 'free', critique_rounds: 2 }) (Python productions.create(brief, director="free", critique_rounds=2)); unealta de agent creative_produce (director, critique_rounds).
Montaj din materialele încărcate (kind: "cut")
kind: "cut" transformă materialele tale încărcate într-un video montat. asset_ids conține 1–10 dintre activele tale gata, video, audio și imagini (cel puțin unul video sau audio, cel mult 60 de minute de material în total; imaginile devin b-roll și referințe). Etapele sunt ingest → music → plan → broll → assemble → render: fiecare sursă e analizată (transcriere, cadre, ritm, fețe), se adaugă un fundal muzical, un apel de planificare alege segmentele (cod determinist alege cadrele, niciodată în mijlocul unui cuvânt), ezitările, pauzele lungi și dublele mai slabe sunt tăiate, b-roll-ul vine din filmările tale, apoi din stoc, apoi din imagini generate (fotografiile tale ca referințe, cu o mișcare Ken Burns; Veo doar cu broll.generated: "veo"), se adaugă subtitrări și reîncadrări animate pentru fiecare format și se pune în coadă o randare de probă a formatului default_format. Rezultatul e o creație editabilă nouă cu formatele 9x16, 1x1 și 16x9 (sau cele pe care le ceri).
curl https://ai.codai.ro/v1/studio/productions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Idempotency-Key: cut-001" \
-H "Content-Type: application/json" \
-d '{"kind":"cut","brief":"Teaser de vlog de 45 s pentru Instagram, păstrează gluma cu cafeaua","language":"ro",
"asset_ids":["<id activ video>","<id activ foto>"],"target_duration_s":45,"pacing":"punchy",
"music":{"source":"lyria"},"broll":{"own":true,"stock":true,"generated":"images"}}'
# 202 { "id": "…", "object": "studio.production", "kind": "cut", "status": "running", "phase": "ingest",
# "ingest": { "total": 1, "done": 0, "failed": 0 }, "ingest_quote_micro_usd": "…", ... }Interoghezi GET /v1/studio/productions/{id} (o sursă lungă poate dura aproape o oră; procesul de fundal o termină și dacă nu întreabă nimeni). Fiecare decizie automată e înregistrată cu motivul ei: GET /v1/studio/productions/{id}/cut întoarce decisions (filler, silence, retake, hook, broll, zoom, reframe, music, script_gap; multicam: angle, split, sync), grupurile de duble clusters cu fiecare variantă și cea aleasă, script_gaps (cu script) și stats. Schimbi oricare dintre ele gratuit cu o revizie — fără apel de model, nu se facturează nimic:
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/revise \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"disabled":["filler:f3","+zoom:2"],"takes":{"c1":"s7"},"pacing":"calm","base_seq":2}'
# 200 { "creative_id": "…", "seq": 3, "branch": "main", "decisions": [...], "stats": {...}, "warnings": [] }Id-ul unei decizii în disabled o dezactivează, +<id> o reactivează (cumulativ între revizii). Versiunea nouă ajunge pe ramura montajului cât timp nimeni nu a editat creația, altfel pe o ramură nouă cut-<n>, ca modificările tale să nu fie niciodată suprascrise. Prețuri: apelul de planificare e taxat pe tokeni (codai::production-cut-plan, cache de prompt activ), fundalul Lyria Pro, imaginile b-roll și randarea de probă la prețurile obișnuite, toate plafonate de budget_micro_usd (implicit 3 $, maxim 20 $); analiza surselor neanalizate încă se facturează pe fiecare activ și e arătată dinainte în ingest_quote_micro_usd. SDK-uri: productions.create({ kind: 'cut', … }), productions.cut(id), productions.reviseCut(id, body) (Python productions.create_cut, cut, revise_cut).
Editări prin prompt
Spune în cuvinte ce vrei, în loc să alegi id-uri de decizii: prompt pe aceeași revizie. Cererile de durată, energie, subtitrări, b-roll și încadrare se aplică gratuit, fără apel de model („fă-l de 60 s”, „make it 30 s”, „mai dinamic” → stilul Fast social, „mai calm” → Minimal, „fără subtitrări”, „fără b-roll”, „pătrat” / „vertical” / „orizontal”). Cererile despre conținut — „doar partea despre preț”, „începe cu întrebarea despre preț”, „scoate introducerea” — costă un apel de model (ofertă 0,15 $, de obicei mai puțin; din alocarea planului, altfel din portofel, 402 insufficient_funds când nu ajunge), care răspunde cu propozițiile de păstrat. Nu se scrie nimic când răspunsul e inutilizabil (502 revise_failed).
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/revise \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"prompt":"doar partea despre preț, cam 45 s","base_seq":3}'
# 200 { "creative_id": "…", "seq": 4, "branch": "main", "decisions": [...], "stats": {...}, "warnings": [],
# "prompt": { "path": "model", "understood": ["target 45 s", "story edit"],
# "explanation": "Am păstrat cele patru propoziții despre preț.", "unmet": null, "cost_micro_usd": 61000 } }Aceleași modificări se pot trimite direct, mereu gratuit: target_s (15–600; se scot întâi propozițiile cele mai puțin importante — incomplete, cea mai slabă dublă, cele mai scurte, cele de la final — niciodată deschiderea sau încheierea; null revine la ținta producției), plan_delta (keep_only, drop, hook, order cu id-uri de propoziții din GET …/cut; null anulează toate editările de poveste), default_format și broll. Unealta agentului: creative_cut_revise cu prompt.
Înregistrări cu mai multe camere
Un podcast sau un interviu filmat cu mai multe camere e un singur montaj: pui fiecare cameră în asset_ids și în multicam.angles (2–4). Camera master_audio (implicit primul unghi) dă sunetul și povestea — doar ea e transcrisă și planificată; celelalte camere sunt doar imagine: un unghi încă neanalizat e analizat fără recunoașterea vorbirii, deci costă mai puțin (încarcă celelalte camere cu ingest: false ca să beneficiezi de asta; un unghi deja analizat e refolosit așa cum e). Fiecare unghi se sincronizează cu master-ul după sunet, gratuit: întâi o corelație a anvelopelor de volum pe care analiza le păstrează deja, apoi o trecere GCC-PHAT la nivel de eșantion pe audio, cu corecția derivei de ceas (camerele obișnuite derivă 10–100 ppm; o diferență 29,97/30 fps înseamnă ~1000 ppm). Montajul urmează apoi reguli simple pe replicile din master: prim-planul vorbitorului puțin înaintea primului cuvânt, niciun cadru mai scurt decât minimul ritmului, planul larg când vorbesc mai mulți deodată, ecran împărțit (stânga/dreapta în 16:9, sus/jos în 9:16) la un schimb rapid de replici, un scurt plan de reacție în monologurile lungi. Vorbitorii sunt asociați prim-planurilor după camera care îi aude cel mai tare, iar planul larg după fețele din cadru.
curl https://ai.codai.ro/v1/studio/productions \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"kind":"cut","brief":"Clip de 60 s cu discuția despre prețuri","asset_ids":["<larg>","<cam-ana>","<cam-mihai>"],
"multicam":{"angles":["<larg>","<cam-ana>","<cam-mihai>"],"master_audio":"<larg>"}}'Fiecare cadru e o decizie (angle:<n>, split:<n>; dacă oprești una rămâne cadrul anterior) și fiecare sincronizare o intrare sync:<asset> cu metoda, deriva și încrederea (sub 30 % montajul avertizează sync_low_confidence:<asset>); GET …/cut listează și camerele multicam. Fixezi gratuit un cadru cu revizia "angles": {"angle:3": "<cam-ana>"} ("wide", "split" sau null ca să scoți fixarea). Când sincronizarea sau asocierea automată greșește, trimite offsets_ms (resursă → ms master minus ms unghi), speaker_angles (S1 → resursă) sau wide_angle; "split": false dezactivează ecranul împărțit. SDK-uri: multicam la crearea montajului, angles la reviseCut / revise_cut; unelte de agent creative_produce (multicam) și creative_cut_revise (angles).
Variante notate
POST /v1/studio/productions/<id>/cut/variants propune montaje alternative ale unui montaj terminat și le notează. Un apel de model propune n variante (implicit 3, maximum 5), fiecare cu alt unghi — strongest_hook (deschide cu cea mai puternică afirmație sau întrebare de sine stătătoare), story (context, idee, concluzie), punchy (cea mai scurtă versiune care încă transmite mesajul), topic (un singur subiect) — iar un al doilea apel notează fiecare variantă și montajul tău curent de la 0 la 99 pe patru axe: Hook (primele secunde îl fac pe privitor să rămână), Flow (o idee clară, fără începuturi din mijlocul propoziției, cu o concluzie), Value (util pentru publicul din brief) și Fit (durată, ritmul vorbirii și tempo pentru format). Fiecare axă combină 60 % evaluatorul cu 40 % trăsături măsurate; totalul e 0,35 Hook + 0,25 Flow + 0,25 Value + 0,15 Fit, iar lista vine de la cea mai bună, fiecare notă cu un motiv pe axă, axa cea mai slabă și un fix_hint. Volumul și controversa nu sunt niciodată răsplătite. Notele sunt predicții, nu garanții.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/variants \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" -d '{"n":3}'
# 200 { "object": "studio.production.cut_variants", "base_seq": 4, "stale": false, "judged": true,
# "variants": [ { "id": "v2", "angle": "strongest_hook", "title": "Întâi întrebarea despre preț", "output_ms": 42100,
# "plan_delta": { "keep_only": ["s7", "s8", "s12"], "hook": "s7" },
# "score": { "total": 81, "hook": 90, "flow": 75, "value": 80, "fit": 60, "source": "blend",
# "weakest_axis": "fit", "fix_hint": "fă-l de 35 s", "reasons": { ... } } }, ... ],
# "current": { "output_ms": 61000, "score": { "total": 58, ... } }, "cost_micro_usd": 41000, "warnings": [] }Ambele apeluri sunt taxate pe tokeni (codai::production-cut-variants, codai::production-cut-score, cache de prompt activ), fiecare cotat și rezervat separat (întâi din alocarea planului, apoi din portofel): lipsa fondurilor înainte de primul apel e un 402 și nu rulează nimic; înainte de evaluator variantele păstrează nota măsurată gratuită (source: "heuristic", avertismentul judge_insufficient_funds). "judge": false sare peste evaluator. Fiecare variantă e un plan_delta la nivel de propoziție, asamblat gratuit, iar rezultatul se salvează pe producție: GET …/cut/variants îl citește din nou gratuit (404 no_variants înainte de primul apel; stale: true după ce montajul s-a schimbat). POST …/cut/select {variant, base_seq?} scrie o variantă ca versiune nouă, gratuit, după aceleași reguli de ramură ca o revizie; anulezi cu plan_delta: null la revizie. Ca să ridici axa cea mai slabă trimite fix_hint ca prompt la …/cut/revise. SDK-uri: productions.cutVariants(id, { n }), retrieveCutVariants(id), selectCutVariant(id, { variant }) (Python cut_variants, retrieve_cut_variants, select_cut_variant); unelte de agent creative_cut_variants și creative_cut_select.
Hook A/B
POST /v1/studio/productions/<id>/cut/hooks propune 2–3 alte propoziții din înregistrare care ar putea deschide același montaj: corpul rămâne neschimbat, se schimbă doar deschiderea. Clasamentul e gratuit și măsurat — o întrebare, o cifră sau o afirmație la început, scurtă și apropiată de brief — iar deschiderea folosită acum e mereu în listă, cu current: true. "judge": true adaugă un apel de model plătit (codai::production-cut-hooks, cotat la 60.000 µUSD, întâi din alocarea planului, apoi din portofel) care le reordonează și scrie câte un why de un rând; lipsa fondurilor păstrează clasamentul gratuit (avertismentul judge_insufficient_funds). Rezultatul se salvează: GET …/cut/hooks îl citește din nou gratuit (404 no_hooks înainte de primul apel, stale: true după ce montajul s-a schimbat). Notele sunt predicții, nu garanții.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/hooks \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" -d '{"n":3,"judge":false}'
# 200 { "object": "studio.production.cut_hooks", "base_seq": 4, "stale": false, "judged": false,
# "hooks": [ { "id": "h1", "sentence": "s7", "text": "De ce să plătești de două ori aceeași factură?", "duration_ms": 2100,
# "score": 86, "reasons": ["question", "brief"], "why": null, "current": false }, ... ],
# "cost_micro_usd": 0, "warnings": [] }Aplici o deschidere cu POST …/cut/revise {"hook": "s7"} (gratuit; "hook": null scoate deschiderea) sau randezi toate deschiderile pentru un test A/B cu Randează tot, mai jos.
Durate presetate
GET /v1/studio/productions/<id>/cut/lengths reasamblează gratuit montajul la 15, 30, 60 și 90 de secunde și max (180 s, limita YouTube Shorts / Instagram Reels). Se scot întâi propozițiile cele mai puțin importante; deschiderea, hook-ul și încheierea rămân. Fiecare preset are output_ms real, propozițiile păstrate, o notă măsurată 0–99, fits_material (materialul e mai scurt decât presetul), trimmed și over_caps — platformele a căror limită de durată o depășește: Shorts și Reels 3:00, X 2:20 (fără Premium), TikTok 10:00 (încărcare prin API), LinkedIn 15:00. Aceeași listă se calculează și pentru montajul curent.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/lengths -H "Authorization: Bearer $CODAI_API_KEY"
# 200 { "object": "studio.production.cut_lengths", "seq": 4,
# "current": { "output_ms": 201000, "preset": null, "over_caps": ["shorts", "reels", "x"] },
# "presets": [ { "id": "30", "target_s": 30, "output_ms": 29400, "sentences": 7, "score": 71,
# "fits_material": false, "over_caps": [], "trimmed": true }, ... ] }Aplici unul cu POST …/cut/revise {"target_s": 30} (gratuit; max = 180).
Ghid de atenție (euristic)
GET /v1/studio/productions/<id>/cut/attention (gratuit) întoarce o curbă pe secundă, 0–1, a montajului curent, construită doar din semnale măsurate — ritmul vorbirii, energia vocii și schimbarea imaginii (tăieturi, b-roll, zoom-uri) — cu semnalele însele, media și fiecare scădere de cel puțin 2 s. Fiecare scădere are motivul probabil (reason: silence, low_energy, slow_speech, static_visual) și, când există, o reparare dintr-un clic (fix) al cărei corp revise îl trimiți ca atare la …/cut/revise (tai pauza, adaugi b-roll sau un zoom, ritm mai alert, scoți propoziția). E o euristică, nu o predicție a retenției (heuristic: true): nu există dovezi publicate și evaluate că astfel de indicatori prezic retenția publicului, deci etichetează-l așa în orice interfață.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/attention -H "Authorization: Bearer $CODAI_API_KEY"
# 200 { "object": "studio.production.cut_attention", "heuristic": true, "step_s": 1, "points": [0.82, 0.77, ...],
# "signals": { "speech": [...], "energy": [...], "visual": [...] }, "mean": 0.64,
# "dips": [ { "start_s": 18, "end_s": 21.5, "reason": "silence", "sentence": null,
# "fix": { "action": "cut_pause", "revise": { "disabled": ["+silence:12"] } } } ] }Chip-uri de subiect
POST /v1/studio/productions/<id>/cut/topics rulează un apel de model plătit (codai::production-cut-topics, cotat la 60.000 µUSD, din alocarea planului) care împarte înregistrarea în subiecte etichetate — label sub 40 de caractere, un kind (intro, problem, solution, demo, pricing, story, tips, qa, outro, other), propozițiile, început și sfârșit — și le salvează. {"refresh": false} întoarce gratuit subiectele salvate; GET …/cut/topics le citește mereu gratuit (404 no_topics înainte de primul apel; 502 topics_failed când modelul nu întoarce nimic utilizabil).
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/topics \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" -d '{}'
# 200 { "object": "studio.production.cut_topics",
# "topics": [ { "id": "t1", "label": "De ce sunt respinse facturile", "kind": "problem", "sentences": ["s1", "s2", "s3"],
# "start_ms": 0, "end_ms": 24100 }, { "id": "t2", "label": "Prețuri", "kind": "pricing", ... } ],
# "cost_micro_usd": 3800, "warnings": [] }Un chip e o revizie gratuită: POST …/cut/revise {"topics": ["t1", "t3"]} păstrează doar acele subiecte (hook-ul rămâne dacă e în interiorul lor); {"topics": []} aduce înapoi toate subiectele. Aceleași subiecte devin capitolele podcastului.
Randează tot
POST /v1/studio/productions/<id>/cut/renders {"kind": "hooks" | "lengths", "ids"?, "quality"?} transformă fiecare variantă într-o creație separată și pornește câte o randare, taxată la prețurile obișnuite de randare (draft 0,005 $/s, final 0,01 $/s). hooks folosește id-urile propozițiilor de deschidere salvate (implicit toate), lengths id-urile preseturilor (implicit 15, 30 și 60). Rulează cel mult 3 randări odată, deci un lot care ar depăși limita e refuzat din start (429 too_many_renders), iar portofelul trebuie să acopere tot lotul înainte să se scrie ceva (402). Idempotency-Key reia primul răspuns.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/renders \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" -d '{"kind":"lengths","ids":["15","30","60"],"quality":"draft"}'
# 200 { "object": "studio.production.cut_renders", "kind": "lengths", "quality": "draft", "quote_micro_usd": 525000,
# "renders": [ { "id": "15", "label": "15 s", "creative_id": "…", "render_id": "…", "output_ms": 15200 }, ... ] }Instagram Trial Reels e singurul mod nativ de a testa A/B variante video; YouTube Test & Compare acoperă doar titluri și miniaturi, nu videoclipul în sine.
Pachet de podcast
POST /v1/studio/productions/<id>/cut/podcast împachetează o randare terminată a montajului ca episod de podcast, în fundal, ca un export FCPXML: răspunsul e un 202 Export (kind: "podcast") plus spotify_upload_url și chapters; interoghează GET /v1/studio/exports/<export_id> până la succeeded și descarcă url (<slug>-podcast.zip). Arhiva conține:
episode.mp3— loudness -16 LUFS, vârf real -1 dBTP, etichete ID3v2, coperta și capitolele CHAP/CTOC (Apple Podcasts le citește);mp3{"channels": 1 | 2, "bitrate_k": 128 | 192}, implicit stereo 192 kbit/s;cover.jpg— 3000×3000, un cadru din montaj cu titlul (Apple acceptă pătrat RGB de 1400–3000 px);notes.md— titlu, descriere cu capitoleMM:SS, note de episod și cuvinte-cheie;chapters.txtcând există capitole șiepisode.mp4(randarea;include_video: falseîl lasă afară).
Capitolele vin din subiectele salvate (întâi rulează un apel de subiecte când nu există și notes e activ) și respectă regulile platformelor: cel puțin 3, primul la 00:00, la cel puțin 30 s distanță, titluri sub 40 de caractere — altfel nu există capitole (avertismentul chapters_too_few). Spotify nu are API de încărcare: încarcă de mână episode.mp4 (podcast video, H.264 16:9 cu AAC) sau episode.mp3 în Spotify for Creators și lipește descrierea din notes.md — Spotify construiește capitolele doar din marcajele de timp din descriere.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/podcast \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" -d '{"notes":true,"mp3":{"channels":1,"bitrate_k":128}}'
# 202 { "id": "…", "object": "studio.export", "kind": "podcast", "status": "queued", "filename": "sfaturi-facturi-podcast.zip",
# "files": ["episode.mp3", "cover.jpg", "notes.md", "chapters.txt", "episode.mp4"],
# "spotify_upload_url": "https://creators.spotify.com/pod/login", "chapters": 4, ... }Costuri: jobul de împachetare e 20.000 µUSD fix (rezervat, taxat când pornește); notes: true (implicit) adaugă un apel pentru note (codai::production-cut-notes, cotat la 30.000 µUSD) — false scrie gratuit note simple. 409 production_not_ready, no_render sau render_not_ready când nu există nimic terminat de împachetat.
Curățarea vocii
Producțiile de montaj curăță gratuit vorbirea: voice_cleanup din cererea de creare e auto (implicit), on sau off. auto măsoară zgomotul de fond dintre cuvinte pentru fiecare sursă și dozează trecerea: -60 dBFS sau mai încet = nimic (deja curat ca pentru difuzare), între -60 și -45 dBFS = o trecere ușoară (intensitate 0,4), peste -45 dBFS = o trecere completă (intensitate 1,0); on curăță mereu (cel puțin trecerea ușoară), off niciodată. Curățarea (denoise DeepFilterNet3, de-esser, compresie, loudness) rulează în etapa opțională clean, între b-roll și asamblare, iar fiecare sursă curățată e o decizie voice:<asset> pe care o poți opri ca pe oricare alta.
POST …/cut/revise {"voice_cleanup": false} oprește vocea curățată; true o pornește la loc — pe un montaj făcut fără surse curățate pornește un job gratuit în fundal, iar răspunsul conține voice_cleanup_job. Interoghează-l cu GET /v1/studio/productions/<job_id> (kind: "cut_clean", phase clean → ready, sources cu floor_db, strength, status și clean_asset_id); când reușește, montajul primește încă o versiune cu vocea curățată.
SDK-uri și unelte de agent (valul 3)
TypeScript: productions.cutHooks(id, { n, judge }), retrieveCutHooks(id), cutLengths(id), cutAttention(id), cutTopics(id, { refresh }), retrieveCutTopics(id), cutRenders(id, { kind, ids, quality }), cutPodcast(id, { notes, mp3 }) (apoi exports.wait) și retrieveCleanJob(id); reviseCut primește hook, topics, drop și voice_cleanup. Python: cut_hooks, retrieve_cut_hooks, cut_lengths, cut_attention, cut_topics, retrieve_cut_topics, cut_renders, cut_podcast; revise_cut(hook=…, topics=…, drop=…, voice_cleanup=…) și create_cut(voice_cleanup=…). Unelte de agent: creative_cut_hooks, creative_cut_lengths, creative_cut_attention, creative_cut_topics, creative_cut_render_all, creative_cut_podcast (extensia VS Code salvează arhiva în .codai/studio/), plus hook / topics / drop / voice_cleanup la creative_cut_revise și voice_cleanup la creative_produce.
Stiluri de editare
Un stil de editare este un set de setări de montaj cu nume: pacing (calm, balanced, punchy), captions (false sau un preset cu max_words_per_line), zoom (off, light, normal, heavy), broll (density none / light / normal / dense, stock true sau false, generated none sau images) și reframe (slow, default, fast). Șase sunt predefinite — podcast-clip, talking-head, tutorial, fast-social, cinematic, minimal — iar GET /v1/studio/edit-styles?lang=en|ro le întoarce cu nume traduse, lângă stilurile tale (adaugă gallery=1 ca să primești și prima pagină din galerie în același apel). Gestionarea stilurilor e gratuită.
- Stilurile tale:
POST /v1/studio/edit-styles {name, description?, knobs}salvează un stil privat (cel mult 50, altfel 409too_many_styles);PATCH /v1/studio/edit-styles/{id}îl modifică, iarDELETEîl șterge. Orice modificare face din nou privat un stil trimis în galerie. - Galerie:
POST /v1/studio/edit-styles/{id}/submittrimite în galerie unul dintre stilurile tale. Se trimit doar numele, descrierea și setările, cu numele tău de utilizator, iar Kodwen îl verifică mai întâi: statusul epending(„În așteptarea verificării”) până îl aprobă un moderator (10 trimiteri pe zi).GET /v1/studio/edit-styles/gallery?limit=&cursor=listează stilurile aprobate, cele mai noi primele, cunext_cursor;POST /v1/studio/edit-styles/{id}/copypune o copie privată în stilurile tale (copied_fromarată sursa). - Raportare și moderare: stilurile din galerie sunt trimise de alți utilizatori și verificate de Kodwen înainte să apară. Raportează un stil care încalcă regulile cu
POST /v1/studio/edit-styles/{id}/report {reason, details?}(spam,harassment,hate,sexual,violence,illegal,ip,other; o raportare pe utilizator, la 5 raportări stilul e ascuns până la verificare). Moderatorii folosescGET /v1/studio/edit-styles/moderation?status=șiPOST /v1/studio/edit-styles/{id}/moderate {action: approve|reject|remove}(doarsuper_admin).
Aplică stilul unui montaj terminat cu un id predefinit sau un id numeric de stil. Se aplică imediat și gratuit: setările se scriu ca versiune nouă, după aceleași reguli de ramură ca o revizie.
curl https://ai.codai.ro/v1/studio/productions/<id>/cut/restyle \
-H "Authorization: Bearer $CODAI_API_KEY" -H "Content-Type: application/json" \
-d '{"style":"fast-social","base_seq":3}'
# 200 { "creative_id": "…", "seq": 4, "branch": "main", "decisions": [...], "stats": {...}, "warnings": [],
# "style": "builtin:fast-social", "broll_slots_needed": 6,
# "broll_job": { "id": "<id job>", "kind": "cut_restyle", "status": "running", "phase": "broll",
# "slots_total": 3, "slots_done": 0, "quote_micro_usd": "…", ... } }B-roll-ul în plus se caută în fundal; imaginile generate se taxează la cost. Când stilul vrea mai mult b-roll decât are montajul, broll_job umple sloturile lipsă din filmările tale, apoi din stoc, apoi cu imagini generate (doar când stilul le permite). Inserturile proprii și din stoc sunt gratuite; imaginile generate sunt acoperite de o singură rezervare din portofel (quote_micro_usd) și se taxează doar ce s-a cheltuit (spent_micro_usd). Interoghezi GET /v1/studio/productions/<id job> până când status e succeeded (a scris încă o versiune, seq) sau failed; un restyle mai nou anulează jobul în curs și eliberează rezervarea. Trimite "fetch_broll": false ca să aplici doar setările; când rezervarea eșuează din lipsă de fonduri, setările se aplică totuși și warnings conține broll_insufficient_funds. GET …/cut arată apoi stilul în overrides.style, cu overrides.pacing_params, overrides.reframe și overrides.broll_limit. SDK-uri: editStyles.list / create / update / delete / submit / gallery / copy / report, productions.restyleCut(id, { style }), productions.retrieveRestyleJob(jobId) (Python edit_styles.*, productions.restyle_cut(id, style), productions.retrieve(job_id)); unelte de agent creative_edit_styles și creative_cut_restyle.
B-roll din stoc
GET /v1/studio/stock/search?q=…&kind=video|photo caută în stocul fără redevențe (întâi Pexels, apoi Pixabay ca rezervă; elementele Pixabay marcate ca generate cu AI sunt eliminate), iar POST /v1/studio/stock/import {provider, id, kind} copiază un element în activele tale, cu licența înregistrată (meta.stock). Ambele sunt gratuite (600 de căutări și 200 de importuri pe utilizator pe zi); 503 stock_unavailable când niciun furnizor nu e configurat, 429 stock_rate_limited când sunt limitați. Montajul folosește aceeași cale pentru b-roll-ul din stoc.
Reguli de atribuire: afișează credit pentru fiecare element (de exemplu „Video by Jane Doe on Pexels”, cu legătură la page_url) oriunde îl arăți și păstrează vizibil credit-ul activului importat acolo unde publici lucrarea — Kodwen îl scrie în creditele randărilor, în paginile partajate și în exporturi. Licențele permit folosirea într-o lucrare nouă, inclusiv comercial, dar nu redistribuirea separată a fișierelor brute, un serviciu de stoc concurent, copierea în masă sau antrenarea de modele; preview_url și files[].url sunt doar pentru afișare temporară. Oamenii, mărcile și locurile din imagini pot necesita propriul tău acord pentru reclame. SDK-uri: stock.search({ q }), stock.import({ provider, id, kind }) (Python stock.search, stock.import_).
Publicare pe YouTube
Publicarea directă este în pregătire: până când Kodwen o activează,
GET /v1/studio/connectionsrăspundeenabled: false, iar conectarea, ciorna și publicarea răspund 404not_found.
Conectezi o dată un canal YouTube, apoi încarci un randament video terminat cu câmpurile pe care le aprobi. Publicarea este gratuită; încărcarea intră în limitele YouTube (Kodwen permite cel mult 100 de încărcări pe zi pe canal).
- Conectare:
POST /v1/studio/connections/youtube/startîntoarce unauthorize_urlGoogle cu unstateși uncode_verifierPKCE; după ce persoana aprobă, trimițicodedin redirecționare laPOST /v1/studio/connections/youtube/exchange {code, state, code_verifier}în cel mult 10 minute. Hub-ul face asta pentru tine în Studio → Conexiuni. Kodwen cere doar permisiunea de încărcare și păstrează token-urile criptate cu o cheie din Cloud KMS.GET /v1/studio/connectionslistează canalele (enabledarată dacă publicarea e activă);DELETE /v1/studio/connections/{id}revocă accesul la Google, șterge token-urile și anulează încărcările nepornite — videoclipurile deja pe YouTube rămân acolo. - Ciornă:
POST /v1/studio/publish/draft {render_id, title, description?, hashtags?, language?}completează titlul, descrierea, etichetele și declarația de conținut AI din randament, fără să încarce nimic. Vizibilitatea și „creat pentru copii” nu sunt niciodată precompletate: le alege persoana. - Încărcare:
POST /v1/studio/publish {render_id, connection_id, title, description?, tags?, privacy, made_for_kids, contains_synthetic_media, publish_at?, category_id?, language?, client_request_id?}răspunde 202 cu o publicare. Câmpurile sunt verificate ca la YouTube: titlu de cel mult 100 de caractere, descriere de cel mult 5000 de octeți, fără<sau>, etichete de cel mult 500 de caractere în total,publish_atdoar cuprivacy: "private". Randamentele făcute cu AI generativ trebuie să păstrezecontains_synthetic_media: true(doar cele care doar editează filmările tale îl pot pune pe false). Trimiteclient_request_idca reîncercările să fie sigure. - Stare:
GET /v1/studio/publish/{id}întoarcephase(queued,uploading,processing,done,failed,cancelled),progress, linkul videoclipului și oerrorlizibilă;POST /v1/studio/publish/{id}/cancelfuncționează până începe încărcarea.
SDK-uri: publishing.listConnections / startConnection / exchangeConnection / deleteConnection / draft / create / retrieve / cancel (Python publishing.list_connections, …); unelte de agent creative_publish_draft (pregătește doar o ciornă — persoana o confirmă în aplicație) și creative_publish_status.
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…, /internal/studio/exports… și /internal/studio/edit-styles… (plus POST /internal/studio/productions/:id/cut/restyle), 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"])Documente
Documente Kodwen 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.
Creații audio
Melodii și podcasturi ca creații editabile în Kodwen Studio — o cronologie multipistă cu secțiuni, piste, clipuri, MIDI, efecte, magistrală master și versuri sincronizate, editată prin operații mici.