Creații
Documente creative editabile în codai Studio — creezi un video, o imagine sau o prezentare ca JSON, le editezi prin operații, păstrezi fiecare versiune și le randezi în MP4.
O creație este un singur document editabil pentru fiecare produs Studio (video, imagine, prezentare, document sau foaie de calcul), păstrat ca JSON simplu — Editable Creative Document (ECD). Agenții și editoarele nu rescriu niciodată HTML: trimit operații mici, adresate prin id. Fiecare editare adaugă o versiune, deci poți oricând să te uiți înapoi, să revii sau să copiezi. Intrarea pentru randare este o funcție pură și deterministă a documentului.
Crearea, citirea și editarea creațiilor sunt gratuite. Costă doar randările și cadrele statice peste cota zilnică gratuită.
| Metodă | 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 |
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,typewritersau cadre cheie) ș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" }'Editarea prin operații
Trimite operațiile față de versiunea pe care ai citit-o (base_seq). Un lot se aplică în întregime sau deloc.
curl https://ai.codai.ro/v1/studio/creatives/$ID/ops \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"base_seq": 3,
"intent": "Titlu mai mare, fundal nou",
"ops": [
{ "op": "setProps", "target": "title", "props": { "size": 120 } },
{ "op": "updateScene", "target": "intro", "patch": { "background": "#101010" } }
]
}'{ "seq": 4, "doc_hash": "9a3f…", "changed": ["title", "intro"], "warnings": [], "rebased": false }Cele 25 de operații: setProps, setLayout, setTiming, setAnim, insertNode, removeNode, moveNode, addScene, removeScene, updateScene, moveScene, setVar, removeVar, setAsset, replaceAsset, setAudioClip, removeAudioClip, setBrand, restyle, adaptFormat, setControl, setLocaleText, setLocale, setLocalization și setMeta. Traducerile se editează cu setLocaleText, setLocale și setLocalization (vezi Localizare). Adaptarea la o platformă este un singur adaptFormat (vezi Platforme); descrierea felului în care se editează o variabilă în panoul de ajustare este un singur setControl (vezi Panoul de ajustare). Schimbarea tokenilor de brand este un singur setBrand; reaplicarea unui brand pe culorile și fonturile literale este un singur restyle (vezi Restilizarea unei creații); înlocuirea unei imagini peste tot este un singur replaceAsset.
Editări simultane
Dacă altcineva a editat creația după ce ai citit versiunea base_seq, gateway-ul îți rebazează operațiile: dacă niciuna dintre țintele și proprietățile pe care le atingi nu s-a schimbat între timp, se aplică peste versiunea curentă (rebased: true). Altfel primești 409 cu ce s-a suprapus — reîncarcă și încearcă din nou:
{
"error": {
"code": "stale",
"type": "invalid_request_error",
"message": "The creative changed since base_seq and 1 change(s) overlap; reload and retry.",
"head_seq": 5,
"conflicts": [{ "target": "title", "head_prop": "props.size", "prop": "props.size" }]
}
}Operații respinse
O operație invalidă, un id necunoscut sau un nod blocat dau 400 cu code-ul operației (invalid, not_found, locked, duplicate_id), path din operație sau document și op_index. Nu se salvează nimic. Trimite force: true ca să editezi intenționat noduri și variabile blocate.
Citire pentru agenți
Documentele mari nu trebuie trimise întregi. outline întoarce câte o linie pentru fiecare scenă și nod (id-uri, tipuri, etichete, text tăiat la 60 de caractere, momente); find întoarce id-uri de noduri după text, tip, scenă, asset sau blocare; subtree întoarce JSON-ul complet doar pentru id-urile cerute.
Versiuni
Fiecare editare este o versiune; istoricul doar crește. revert adaugă o versiune nouă cu un document mai vechi (intent: "revert to N"), deci și anularea, și refacerea sunt reveniri. fork pornește o creație nouă din orice versiune. GET …?seq=N citește o versiune veche.
Previzualizare și randare
GET …/compiled întoarce compoziția HyperFrames deterministă, cu asset-urile tale ca URL-uri semnate valabile o oră, gata pentru un iframe srcdoc (Cache-Control: private, no-store). POST …/render compilează versiunea curentă și pune în coadă un codai-render obișnuit:
curl https://ai.codai.ro/v1/studio/creatives/$ID/render \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "format": "16x9", "quality": "draft" }'Răspunde 202 cu render_id; interoghează-l cu GET /v1/studio/renders/{id}, care raportează creative_id. Randarea se facturează ca orice randare, o singură dată, după ce a reușit. Formatul trebuie să fie 1920×1080, 1080×1920, 1080×1080 sau 1080×1350 (4:5). Randarea funcționează pentru creațiile video (celelalte tipuri răspund 400 not_supported_for_kind); compilarea previzualizează și creațiile de tip prezentare, document și foaie de calcul (vezi Documente).
Randările sunt compuse din segmente per scenă: scenele care nu s-au schimbat de la o randare anterioară se refolosesc, așa că o nouă randare după o editare mică randează doar scenele atinse (taxarea rămâne pe secundă de ieșire). Dacă randezi din nou exact aceeași versiune, același format, aceeași calitate și aceleași fps, răspunsul este imediat 200 cu randarea existentă (cached: true, status: "succeeded") și nu se taxează nimic; corpul 202 al unei randări noi are cached: false.
ai_label (corner, end_card sau none) imprimă în video o etichetă IA vizibilă, ca la randări. Dacă lipsește, este corner când creația folosește narațiune TTS sau imagini generate cu IA, altfel none; none cu narațiune TTS răspunde 400 ai_label_required. Conform Regulamentului UE privind IA (art. 50 alin. 4), tu, ca implementator, rămâi responsabil de etichetarea deepfake-urilor pe care le publici.
Cadre statice
POST …/frame întoarce cadre PNG statice ale creației la cel mult 6 momente, pentru un format (implicit formatul implicit al documentului) și o versiune (implicit cea curentă):
curl https://ai.codai.ro/v1/studio/creatives/$ID/frame \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "t_ms": [0, 1500, 4200], "format": "16x9", "exact": false }'| Câ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 | Nodul sau scena selectată (implicit: scena care rulează la t_ms) |
apply | true (implicit) scrie o versiune; false întoarce doar ops propuse |
Răspunde 200 { "head_seq": 4, "applied_ops": 1, "summary": "Am mărit titlul.", "version": { "seq": 4, "doc_hash": "…" } }. O modificare suprapusă de la base_seq răspunde 409 stale (reîncarcă, reîncearcă); când comentariul nu cere o editare răspunsul este 422 no_edit, cu replica modelului în error.text; după 60 s, 504 timeout. Operațiile respinse primesc automat o rundă de reparare.
Preț. Facturat pe tokeni ca orice apel text codai (rânduri de utilizare codai::creative-comment, cache de prompt activ) — un comentariu obișnuit costă mult sub 0,01 $.
Variante A/B
POST …/variants cere unei ture scurte de agent pe server variante A/B ale creației la base_seq. Fiecare variantă schimbă doar aspectele din focus și este salvată ca o creație nouă derivată din base_seq (titlul "<titlu> — <etichetă>", operațiile aplicate ca versiunea 2 de agent), deci o poți deschide, edita, randa sau șterge ca pe orice creație. Originalul nu se schimbă până nu alegi o câștigătoare.
curl https://ai.codai.ro/v1/studio/creatives/$ID/variants \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "count": 3, "focus": ["hook", "cta"], "brief": "Black Friday, public tânăr", "base_seq": 4 }'| Câ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 codai (rânduri de utilizare codai::creative-variants, cache de prompt activ — mult sub 0,05 $ pentru trei variante); fiecare randare draft se facturează la interogare ca orice randare draft (0,005 $ pe secundă de ieșire, minimum 0,025 $). Listarea și alegerea sunt gratuite.
Kituri de brand
Un kit de brand este un set reutilizabil de tokeni de brand — cinci roluri de culoare primary, secondary, accent,
background, text, până la 12 culori suplimentare cu nume, familiile de fonturi heading și body și o rază de
colț opțională — plus un logo opțional. Kiturile sunt salvate ca tokeni de design DTCG
(formatul 2025.10), așa că le poți importa din instrumentele de design și le poți exporta către ele.
Formatul DTCG
{
"$schema": "https://www.designtokens.org/schemas/2025.10/format.json",
"color": {
"$type": "color",
"primary": { "$value": { "colorSpace": "srgb", "components": [0.004, 0.439, 0.502], "hex": "#017080" } },
"secondary": { "$value": { "colorSpace": "srgb", "components": [0.137, 0.671, 0.737], "hex": "#23abbc" } },
"accent": { "$value": { "colorSpace": "srgb", "components": [0.902, 0.718, 0.192], "hex": "#e6b731" } },
"background": { "$value": { "colorSpace": "srgb", "components": [0.969, 0.973, 0.98], "hex": "#f7f8fa" } },
"text": { "$value": { "colorSpace": "srgb", "components": [0.059, 0.067, 0.082], "hex": "#0f1115" } },
"sand": { "$value": { "colorSpace": "srgb", "components": [0.949, 0.902, 0.82], "hex": "#f2e6d1" } }
},
"font": {
"$type": "fontFamily",
"heading": { "$value": ["Manrope"] },
"body": { "$value": ["Inter", "Arial"] }
},
"radius": { "$type": "dimension", "base": { "$value": { "value": 8, "unit": "px" } } }
}- Cele cinci roluri de culoare sunt obligatorii. Culorile suplimentare au un nume format dintr-o literă mică urmată de
cel mult 31 de litere mici, cifre sau
-(cel mult 12 culori). - Importul este tolerant:
$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 codai (rând de utilizare codai::brand-kit-extract,
cache de prompt activ — de obicei mult sub 0,01 $, dintr-un URL sau dintr-un logo). Crearea, editarea, exportul,
listarea, citirea, ștergerea, aplicarea kiturilor și restilizarea sunt gratuite.
Subtitrări
POST /v1/studio/creatives/{id}/captions adaugă subtitrări automate unei creații video: transcrie un clip de voce
cu momentul fiecărui cuvânt, corectează ortografia după scenariu când narațiunea este a ta și inserează un nod
captions stilizat în zona sigură a formatului implicit (deasupra interfeței platformei când formatul este legat de o
platformă, vezi Platforme).
curl https://ai.codai.ro/v1/studio/creatives/$ID/captions \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "preset": "karaoke-pop" }'| Câ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 codai (rând de
utilizare codai::adapt-focal, cache de prompt activ — de obicei mult sub 0,01 $ pe apel).
Lint și corecturi
POST /v1/studio/creatives/{id}/lint rulează verificări statice, fără randare. Erorile blochează randarea;
avertismentele îți sunt arătate ție și agenților. Fiecare problemă are forma
{ "code", "severity", "message", "target", "value"?, "format"?, "fixable"? } — format numește formatul la care se
referă o problemă specifică unui format, iar fixable: true marchează problemele pe care corectura automată gratuită
le poate rezolva — iar răspunsul adaugă fixable, numărul acestor probleme.
| 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) |
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
codai (rând de utilizare codai::lint-fix, cache de prompt activ — de obicei mult sub 0,01 $).
Panoul de ajustare
Editoarele din hub, desktop și telefon arată un panou de ajustare pentru variabilele documentului (vars): câte un
selector de culoare, un cursor, un comutator, o listă de opțiuni, un câmp text sau un selector de imagine pentru
fiecare variabilă. Variabilele își păstrează tipul și valoarea în vars; meta.controls[<cheie variabilă>] descrie
doar cum se editează fiecare.
{ "op": "setControl", "key": "accent", "control": { "kind": "select", "label": "Accent", "group": "Colours", "options": [{ "value": "#e4002b", "label": "Red" }, { "value": "#1d3557", "label": "Navy" }] } }| Tip de 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": ["codai", "Black Friday"],
"locales": {
"en": {
"tone": "informal",
"texts": { "title:spans.0": { "t": "Autumn sale", "h": "3fa2c9d1e0b4a7f6", "by": "agent" } },
"audio": { "voice-1": "<id asset de voce>" },
"captions": { "captions": [{ "text": "Autumn", "startMs": 0, "endMs": 420 }] }
}
}
}
}
}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": ["codai"],
"total": 18,
"locales": [{ "locale": "en", "tone": "informal", "translated": 17, "missing": 1, "stale": 2, "voice": 0 }]
}Cu ?locale=en&units=1 adaugă units (offset, limit cel mult 500, implicit 200) și units_total: câte o
intrare pentru fiecare unitate, cu key, node_id, kind, text (sursa), max_chars, traducerea curentă
translation (sau null), by, stale și locked. O limbă necunoscută răspunde 404 Locale not found..
Traducerea cu modelul
POST /v1/studio/creatives/{id}/localize traduce creația în cel mult 10 limbi într-un singur apel și salvează
totul ca o singură versiune nouă (autor agent, intenție localize: en, de):
curl https://ai.codai.ro/v1/studio/creatives/$ID/localize \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "locales": ["en", "de"], "tone": "informal", "glossary": ["codai", "Black Friday"] }'| Câ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 codai (rânduri de utilizare codai::localize, cache
de prompt activ — o creație obișnuită costă mult sub 0,01 $ pe limbă). Citirea limbilor, editările manuale și copiile
sunt gratuite.
Voce și subtitrări
Cu voice: true, pentru fiecare clip de voce care este propria ta narațiune Studio (un asset de voce gata, care și-a
păstrat textul sursă), narațiunea este tradusă odată cu textele, reînregistrată cu aceleași setări de voce în limba
țintă și înlocuită doar pentru acea limbă (audio). Când un nod de subtitrare afișează acel clip, vocea nouă este
transcrisă, iar subtitrările traduse sunt resincronizate (captions). Narațiunea se facturează ca orice narațiune, iar
transcrierea ca la Subtitrări (rând de utilizare codai::captions). Un clip care nu poate fi
reînregistrat adaugă avertismentul voice unavailable for <clip> (<locale>); textele sunt traduse oricum.
Editarea manuală a traducerilor
Editările manuale sunt operații obișnuite pe POST …/ops:
{ "op": "setLocaleText", "locale": "en", "texts": { "title:spans.0": "Autumn sale", "doc:title": null } }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: ['codai'] });
const html = await codai.creatives.compiled(c.id, { locale: 'en' });
const job = await codai.creatives.render(c.id, { quality: 'draft', locale: 'en' });
const fork = await codai.creatives.forkLocale(c.id, 'en', { title: 'Teaser de lansare (EN)' });status = client.creatives.locales(c["id"])
done = client.creatives.localize(c["id"], ["en", "de"], tone="informal", glossary=["codai"])
issues = client.creatives.lint(c["id"], locale="en")
fork = client.creatives.fork_locale(c["id"], "en", title="Teaser de lansare (EN)")Agenții de pe desktop, telefon și VS Code folosesc creative_localize (creative_id, locales, tone, glossary,
voice, only_missing) și creative_locale_fork (creative_id, locale, title); creative_lint primește
locale și enumeră codurile de traducere, iar creative_edit acceptă operațiile setLocaleText, setLocale și
setLocalization. Ca să umpli câte o creație pentru fiecare rând dintr-un tabel — opțional într-o altă limbă pe
fiecare rând — vezi Loturi.
Șabloane
Salvează oricare dintre creațiile tale ca șablon privat, reutilizabil: alegi nodurile text și imagine care se schimbă
de la o folosire la alta, iar fiecare devine o variabilă a șablonului. POST /v1/studio/creatives/{id}/template ia
creația de la seq (implicit: capul):
curl https://ai.codai.ro/v1/studio/creatives/$CREATIVE_ID/template \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Promo 9:16", "vars": [
{ "node_id": "n_title", "prop": "text", "key": "headline", "label": "Titlu" },
{ "node_id": "n_photo", "prop": "image", "key": "photo" } ] }'prop: "text"pe un 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.
Dezarhivează-l și deschide project.fcpxml în Final Cut Pro (File → Import → XML) sau în DaVinci Resolve (File
→ Import → Timeline); căile către media sunt relative, așa că păstrează dosarul întreg. Premiere Pro îl deschide
prin importul FCPXML sau după o conversie (de exemplu cu un convertor din FCPXML în XML); CapCut pentru desktop poate
importa fișierele media.
curl "https://ai.codai.ro/v1/studio/creatives/$ID/export.fcpxml?format=16x9" \
-H "Authorization: Bearer $CODAI_API_KEY"
# 202 { "id": "<id export>", "object": "studio.export", "status": "queued", ... }
curl https://ai.codai.ro/v1/studio/exports/$EXPORT_ID \
-H "Authorization: Bearer $CODAI_API_KEY"Implicit, format este primul format al documentului de 1920×1080, 1080×1920 sau 1080×1080, iar seq este versiunea
curentă. Corpul răspunsului 202 este un StudioExport cu câmpurile id, object (studio.export), status,
creative_id, seq, format, fps, filename, url, expires_at, bytes, rendered_seconds, cost_micro_usd,
warnings, error și created_at. Interoghează GET /v1/studio/exports/{id} (răspunde mereu 200 cât timp exportul
e al tău) până când status devine succeeded — atunci url este un link de descărcare semnat, valabil o oră (fiecare
interogare semnează unul nou) — sau failed, cu error.code și error.message. Asset-urile audio lipsă sau care nu
sunt gata sunt sărite și apar în warnings. Erori: 400 unsupported_kind (creația nu e video),
400 unsupported_format, 400 too_long (peste 180 s), 404 (creație sau seq necunoscut), 429 too_many_renders
(exporturile se numără împreună cu randările la limita de 3 lucrări în curs).
const queued = await codai.creatives.exportFcpxml(c.id, { format: '16x9' });
const done = await codai.exports.wait(queued.id); // interoghează la 3 s, cel mult 15 min
if (done.status === 'succeeded') console.log(done.filename, done.url, done.expires_at, done.cost_micro_usd);
else console.error(done.error?.code, done.error?.message);queued = client.creatives.export_fcpxml(c["id"], format="16x9")
done = client.exports.wait(queued["id"]) # interoghează la 3 s, cel mult 15 min
if done["status"] == "succeeded":
print(done["filename"], done["url"], done["expires_at"], done["cost_micro_usd"])Preț. Segmentele sunt aceleași pe care le folosesc randările finale: scenele deja randate (de un export anterior
sau de o randare finală) se refolosesc gratuit. Se facturează doar secundele randate acum, la prețul randării
finale, o singură dată, după ce exportul a reușit (rendered_seconds, cost_micro_usd); un export făcut numai din
segmente din cache nu costă nimic. Interogarea este gratuită.
Export Lottie
Folosește o creație video pe web sau într-o aplicație ca animație vectorială ușoară: GET /v1/studio/creatives/{id}/export.lottie o exportă în format Lottie, sincron și gratuit — fără randare, fără coadă.
container=json(implicit) — un singur JSON Bodymovin cu imaginile încorporate ca data URL. Rulează în lottie-web, în playerele Lottie pentru Android și iOS și în importatoarele pentru After Effects.container=dotlottie— o arhivă dotLottie v2 (.lottie:manifest.json,a/<format>.json,i/<asset>.<ext>), mai mică, pentru playerele dotLottie.formats=all— toate formatele documentului (dotLottie: câte o animație pe format; JSON: o arhivă zip cu fișierele JSON). Fără el,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 |
Proveniență. Fiecare fișier spune că a fost făcut cu AI: meta din JSON conține g: "codai Studio",
tc: "AI-generated", ai_generated: true și custom: { ai_generated: true, generator: "codai-studio", doc_hash }, iar
fiecare răspuns 2xx are antetele x-codai-ai-generated: true și x-codai-generator: codai/codai-studio. Erori:
400 unsupported_kind (creația nu e video), 400 invalid_request, 404 (creație sau seq necunoscut),
413 too_large (fișierul ar depăși 20 MB).
const e = await codai.creatives.exportLottie(c.id, { container: 'dotlottie', formats: 'all' });
console.log(e.filename, e.url, e.warnings);
const file = await codai.creatives.downloadLottie(c.id, { format: '16x9' }); // { bytes, filename, contentType }e = client.creatives.export_lottie(c["id"], container="dotlottie", formats="all")
print(e["filename"], e["url"], e["warnings"])
f = client.creatives.download_lottie(c["id"], format="16x9") # {"bytes", "filename", "content_type"}Preț. Gratuit: fără randare, fără rând de consum, fără plafon de cheltuieli.
Documente (prezentare, document, foaie de calcul)
Documentele Studio — prezentările (PPTX), documentele (DOCX) și foile de calcul (XLSX) — pot fi
editate și ele ca o creație. Creația păstrează același conținut ca spec-ul documentului, sub formă de noduri cu tip
pe care le editezi prin operații, le previzualizezi live și le generezi înapoi într-un fișier.
Reprezentarea.
- Prezentare — câte o scenă pe slide (
s1,s2, …), cu aranjarea slide-ului î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 $).
Cronologiile devin creații
POST /v1/studio/timelines funcționează ca înainte și acum salvează aceeași cronologie și ca o creație editabilă: corpul 202 are creative_id (sau null dacă copia nu a putut fi făcută — randarea nu este niciodată afectată).
Partajare, revizuire și remix
O creație poate fi partajată printr-un link de vizualizare public, doar pentru citire (oricine are o cheie API o poate remixa în propriul cont, cu resursele copiate), sau printr-un link de revizuire, unde persoane fără cont lasă comentarii ancorate pe un nod și pe un moment, iar aprobatorii aprobă sau cer modificări; o aprobare fixează versiunea. Totul este gratuit — vezi Partajare, revizuire și remix.
Hub (intern)
Hub-ul apelează aceleași rute sub /internal/studio/creatives…, /internal/studio/brand-kits…, /internal/studio/templates… și /internal/studio/exports…, cu un token de serviciu și un user_id; corpurile și erorile sunt identice. Kiturile de brand sunt oglindite complet: POST /internal/studio/brand-kits, PATCH /internal/studio/brand-kits/:id, GET /internal/studio/brand-kits/:id/dtcg, POST /internal/studio/brand-kits/extract (cu url sau logo_asset_id) și POST /internal/studio/creatives/:id/restyle. Sunt oglindite și subtitrările, platformele, corecturile și panoul de ajustare: POST /internal/studio/creatives/:id/captions, GET /internal/studio/creatives/:id/captions.srt și .vtt, POST /internal/studio/creatives/:id/adapt, POST /internal/studio/creatives/:id/lint/fix și GET /internal/studio/creatives/:id/controls. Este oglindită și localizarea: GET /internal/studio/creatives/:id/locales, POST /internal/studio/creatives/:id/localize și POST /internal/studio/creatives/:id/locales/:locale/fork; loturile se află sub /internal/studio/batches… (vezi Loturi); linkurile de partajare, revizuirile și remixul se află sub /internal/studio/creatives/:id/shares…, /internal/studio/creatives/:id/review… și /internal/studio/shares/:token/remix (vezi Partajare, revizuire și remix).
SDK-uri
const c = await codai.creatives.create({ kind: 'video', title: 'Teaser de lansare' });
const r = await codai.creatives.edit(c.id, {
baseSeq: c.head_seq,
ops: [{ op: 'setMeta', title: 'Teaser de lansare v2' }],
});
const job = await codai.creatives.render(c.id, { quality: 'draft' });
let frames = await codai.creatives.frame(c.id, { t_ms: [0, 1500] });
while (frames.status === 'running') {
await new Promise((r) => setTimeout(r, 2000));
frames = await codai.creatives.frameJob(c.id, frames.frame_job_id);
}
const edit = await codai.creatives.comment(c.id, { t_ms: 1500, comment: 'Fă titlul roșu', base_seq: 2 });
const ab = await codai.creatives.variants(c.id, { base_seq: edit.head_seq, count: 3, focus: ['hook', 'cta'] });
await codai.creatives.pickVariant(c.id, ab.variants[0].creative_id, { base_seq: edit.head_seq });
const kit = await codai.brandKits.extract({ url: 'https://acme.example/' });
const branded = await codai.creatives.create({ kind: 'image', brand_kit_id: kit.id });c = client.creatives.create({"kind": "video", "title": "Teaser de lansare"})
client.creatives.edit(c["id"], base_seq=c["head_seq"], ops=[{"op": "setMeta", "title": "v2"}])
job = client.creatives.render(c["id"], {"quality": "draft"})
frames = client.creatives.frame(c["id"], t_ms=[0, 1500], exact=True)
if frames["status"] == "running":
frames = client.creatives.frame_job(c["id"], frames["frame_job_id"]) # interoghează până la "succeeded"
edit = client.creatives.comment(c["id"], t_ms=1500, comment="Fă titlul roșu", base_seq=2)
ab = client.creatives.variants(c["id"], base_seq=edit["head_seq"], count=3, focus=["hook", "cta"])
client.creatives.pick_variant(c["id"], ab["variants"][0]["creative_id"], base_seq=edit["head_seq"])Documente
Documente codai Studio — construiești prezentări (PPTX), documente (DOCX) și foi de calcul (XLSX) dintr-un spec JSON, le previzualizezi ca PDF și PNG și transformi prezentările în MP4-uri narate.
Loturi
Umpli un șablon sau o creație dintr-un fișier CSV ori dintr-o foaie de calcul — câte o creație pe rând, cu limbă și randare opționale pe rând, export de stare și o arhivă ZIP cu videoclipurile.