Kodwen docs
Gateway

Asset-uri

Conținutul media Kodwen Studio stocat — listezi, obții un URL semnat proaspăt, descarci sau ștergi imaginile, muzica și video-urile generate.

Un asset este un fișier generat pe care gateway-ul îl păstrează pentru tine: fiecare imagine, clip muzical sau video produs cu response_format: "url" și fiecare job video terminat (MP4-ul e stocat mereu). Asset-urile aparțin utilizatorului care deține cheia API — id-ul asset-ului altui utilizator întoarce 404 — și se păstrează 90 de zile.

În loc de octeți base64 primești un obiect asset cu un URL semnat valabil o oră. URL-ul nu cere cheia API, așa că îl poți da unui browser, unui element <audio>/<video> sau unui utilitar de descărcare. Când expiră, ceri unul nou.

Agenții răsfoiesc biblioteca prin uneltele serverului MCP studio_assets_list (kind, before, limit) și studio_renders_list (before, limit, root_render_id). Ambele sunt doar citire și nu întorc niciodată URL-uri semnate; generarea media costă bani și nu e disponibilă prin ele.

Obiectul asset

{
  "asset_id": "7d0f2c1e-4b8a-4c1e-9a51-0f3c2e8d9b10",
  "kind": "video",
  "status": "ready",
  "model": "veo-3.1-fast-generate-001",
  "url": "https://storage.googleapis.com/…&X-Goog-Signature=…",
  "mime_type": "video/mp4",
  "bytes": 1834221,
  "duration_seconds": 4,
  "width": null,
  "height": null,
  "created_at": "2026-10-10T08:00:00.000Z",
  "expires_at": "2026-10-10T09:00:00.000Z"
}
CâmpSemnificație
asset_idUUID stabil. Îl folosești ca să obții un URL proaspăt sau ca să ștergi asset-ul.
kindmusic, video, image, speech, render, upload sau sfx.
statuspending, ready, failed sau deleted. Doar asset-urile ready au url.
modelModelul upstream care l-a produs.
urlURL semnat al octeților, valabil o oră; null dacă nu e ready. Nu-l păstra în cache după expires_at.
mime_type, bytesTipul de conținut și mărimea obiectului stocat.
duration_secondsDurata audio sau video; null pentru imagini.
width, heightPixeli, când sunt cunoscuți; altfel null.
created_atMomentul creării. Asset-ul e șters după 90 de zile.
expires_atMomentul în care url nu mai funcționează; null când nu există URL.

Endpoint-uri

MetodăCaleRezultat
GET/v1/assets?kind=&before=&limit={ object: "list", data: [asset…], next_before }, cele mai noi primele
GET/v1/assets/{id}Un asset cu un url semnat proaspăt
GET/v1/assets/{id}/contentRedirecționare 302 către URL-ul semnat
DELETE/v1/assets/{id}{ id, object: "asset", deleted: true }
POST/v1/assets/uploads201 sesiune de upload { id, upload_url, chunk_granularity, max_bytes, expires_at }
POST/v1/assets/uploads/{id}/complete{ asset, ingest: { status, quote_micro_usd? } }
GET/v1/assets/quota{ used_bytes, limit_bytes, max_file_bytes }
GET/v1/assets/{id}/ingest{ status, error?, summary?, result?, sprite_url? }
POST/v1/assets/{id}/ingest202 { status, quote_micro_usd? }
GET/v1/assets/search?q=&limit={ object: "list", data: [{ asset_id, t_ms, kind, score, text? }] }

Listare

kind filtrează după tip, limit e între 1 și 100 (implicit 30). Pentru paginare, trimite next_before din răspunsul anterior ca before, până când next_before devine null.

curl "https://ai.codai.ro/v1/assets?kind=video&limit=20" \
  -H "Authorization: Bearer $CODAI_API_KEY"

Obținerea octeților

GET /v1/assets/{id} întoarce asset-ul cu un URL semnat nou. GET /v1/assets/{id}/content face același lucru și redirecționează către el, util cu curl -L. Redirecționarea are cache-control: private, no-store. Un asset fără conținut încă (pending) întoarce 404 pe /content.

curl -L -o plane.mp4 "https://ai.codai.ro/v1/assets/$ASSET_ID/content" \
  -H "Authorization: Bearer $CODAI_API_KEY"

Ștergere

DELETE /v1/assets/{id} șterge fișierul stocat și marchează asset-ul deleted; URL-urile deja emise nu mai funcționează. Ștergerea nu rambursează generarea.

Urcarea propriilor fișiere media

Poți urca video, audio și imagini de cel mult 4 GiB pe fișier și 50 GiB per utilizator (GET /v1/assets/quota). Octeții merg direct în Google Cloud Storage printr-o sesiune reluabilă, niciodată prin gateway:

  1. POST /v1/assets/uploads cu { filename, mime, bytes, ingest?, origin? } întoarce un upload_url. E un secret bearer pentru acel obiect — nu-l scrie în loguri. Trimite origin când urci din browser.
  2. Trimite octeții cu PUT la upload_url, în bucăți multiple de 262144 octeți (8 MiB merge bine), fiecare cu Content-Range: bytes <primul>-<ultimul>/<total> și fără antet Authorization. 308 înseamnă „continuă”: antetul Range: bytes=0-<n> arată cât a primit stocarea. Ultima bucată răspunde 200/201. După o eroare de rețea, trimite PUT cu corp gol și Content-Range: bytes */<total> și continuă de la Range primit.
  3. POST /v1/assets/uploads/{id}/complete verifică mărimea, marchează asset-ul ready (kind: "upload") și pornește ingestia dacă ingest a fost true (implicit). 409 upload_incomplete înseamnă că nu au ajuns încă toți octeții.

Tipuri acceptate: video/mp4, video/quicktime, video/webm, video/x-matroska, audio/mpeg, audio/wav, audio/x-wav, audio/mp4, audio/aac, audio/flac, audio/ogg, audio/webm, image/png, image/jpeg, image/webp, image/gif, image/heic.

Ingestie și căutare

Ingestia analizează o singură dată un fișier video sau audio urcat, ca Studio și agenții să-l poată edita după conținut: transcriere cu timpi pe cuvânt și vorbitori, planuri cu un sprite sheet de cadre-cheie, beat-uri, urmărirea fețelor și a subiecților, semnale de loudness/claritate și embedding-uri pentru căutare. Prețul e pe minut de media și trece prin poarta de plată: suma e cotată și blocată în portofel înainte de pornirea jobului și decontată la final. Cu sold insuficient, complete raportează ingest.status: "insufficient_funds" (asset-ul rămâne ready), iar POST /v1/assets/{id}/ingest răspunde 402 insufficient_funds.

GET /v1/assets/{id}/ingest întoarce status (none, queued, running, succeeded, failed, insufficient_funds, unsupported); după succeeded include și summary, result complet (ingest.json, versiunea 1) și sprite_url (1 h). GET /v1/assets/search?q=mașină%20roșie găsește momente (t_ms) în toate fișierele tale analizate, potrivind cadre-cheie și segmente de transcriere.

Crearea asset-urilor

Cere response_format: "url" pe oricare endpoint media (vezi Embeddings, audio și realtime):

  • POST /v1/images/generations și POST /v1/images/edits — câmp în body;
  • POST /v1/audio/music — câmp în body;
  • GET /v1/videos/{id} — parametrul de query ?response_format=url.

Valoarea implicită rămâne b64_json, deci clienții existenți nu văd nicio schimbare. Video-urile se taxează exact o dată, iar MP4-ul lor e stocat indiferent de formatul cu care interoghezi.

Fiecare randare Studio terminată e stocată tot ca asset și apare la kind=render (GET /v1/assets?kind=render).

Efectele sonore importate din biblioteca gratuită de efecte sonore CC0 sunt asset-uri de tip kind=sfx.

Erori

HTTPcodeCând
400bad_requestkind, before (trebuie să fie dată-oră ISO) sau limit invalid.
404not_foundId necunoscut, asset-ul altui utilizator sau /content pe un asset fără conținut.
400invalid_originorigin din upload nu e o origine de browser permisă.
402insufficient_fundsPortofelul nu acoperă cotația ingestiei.
409upload_incomplete / asset_not_readycomplete înainte să ajungă toți octeții; ingestie pe un asset care nu e ready.
413file_too_large / storage_quota_exceededPeste 4 GiB pe fișier sau 50 GiB per utilizator.
415unsupported_media_typeUn tip în afara listei de mai sus sau ingestie pe o imagine.
503ingest_unavailable / search_unavailableIngestia sau căutarea nu e configurată pe acest gateway.
503assets_unavailableStocarea asset-urilor nu e configurată pe acest gateway.

SDK-uri

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

const { assets } = await client.music.generate({ prompt: 'calm lo-fi piano', response_format: 'url' });
const url = await client.assets.contentUrl(assets[0]!.asset_id); // URL proaspăt, valabil o oră
for await (const a of client.assets.iterate({ kind: 'video' })) console.log(a.asset_id, a.bytes);
await client.assets.delete(assets[0]!.asset_id);
from Kodwen import Kodwen
client = Kodwen(api_key=os.environ["CODAI_API_KEY"])

r = client.music.generate({"prompt": "calm lo-fi piano", "response_format": "url"})
url = client.assets.content_url(r.assets[0]["asset_id"])
for a in client.assets.iterate(kind="video"):
    print(a["asset_id"], a["bytes"])
client.assets.delete(r.assets[0]["asset_id"])

Ambele SDK-uri au și list(...), retrieve(id), content(id) (descarcă octeții de la URL-ul semnat) și contentPath(id) / content_path(id) (URL-ul de redirecționare autentificat).

Urcarea unui fișier, apoi citirea analizei și căutarea în el:

const file = new Blob([await readFile('interviu.mp4')]);
const { asset, ingest } = await client.assets.upload(file, {
  filename: 'interviu.mp4',
  mime: 'video/mp4',
  onProgress: (sent, total) => console.log(`${Math.round((100 * sent) / total)}%`),
});
const state = await client.assets.ingest(asset.asset_id); // interoghează până când status e 'succeeded'
const hits = await client.assets.search('mașină roșie', { limit: 5 });
done = client.assets.upload("interviu.mp4", on_progress=lambda sent, total: print(sent, total))
state = client.assets.ingest(done["asset"]["asset_id"])
hits = client.assets.search("mașină roșie", limit=5)

Pe această pagină