codai docs
Gateway

Assets

Stored codai Studio media — list, fetch a fresh signed URL, download or delete the images, music and videos you generated.

An asset is a generated file the gateway keeps for you: every image, music clip or video produced with response_format: "url", and every finished video job (the MP4 is always stored). Assets belong to the user that owns the API key — another user's asset id is a 404 — and are kept for 90 days.

Instead of base64 bytes you get an asset object with a signed URL valid for 1 hour. The URL needs no API key, so you can hand it to a browser, an <audio>/<video> element or a download tool. When it expires, ask for a fresh one.

Agents browse the library through the MCP server tools studio_assets_list (kind, before, limit) and studio_renders_list (before, limit, root_render_id). Both are read-only and never return signed URLs; generating media costs money and is not available through them.

The asset object

{
  "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"
}
FieldMeaning
asset_idStable UUID. Use it to get a fresh URL or delete the asset.
kindmusic, video, image, speech, render, upload or sfx.
statuspending, ready, failed or deleted. Only ready assets have a url.
modelUpstream model that produced it.
urlSigned URL of the bytes, valid for 1 hour; null unless ready. Never cache it past expires_at.
mime_type, bytesContent type and size of the stored object.
duration_secondsAudio or video length; null for images.
width, heightPixels, when known; otherwise null.
created_atCreation time. The asset is removed 90 days later.
expires_atWhen url stops working; null when there is no URL.

Endpoints

MethodPathResult
GET/v1/assets?kind=&before=&limit={ object: "list", data: [asset…], next_before }, newest first
GET/v1/assets/{id}One asset with a fresh signed url
GET/v1/assets/{id}/content302 redirect to the signed URL
DELETE/v1/assets/{id}{ id, object: "asset", deleted: true }

Listing

kind filters by kind, limit is 1–100 (default 30). To page, pass the previous response's next_before as before until next_before is null.

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

Fetching the bytes

GET /v1/assets/{id} returns the asset with a newly signed URL. GET /v1/assets/{id}/content does the same and redirects to it, which is handy with curl -L. The redirect carries cache-control: private, no-store. An asset with no content yet (pending) is a 404 on /content.

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

Deleting

DELETE /v1/assets/{id} removes the stored file and marks the asset deleted; URLs already handed out stop working. Deleting does not refund the generation.

Creating assets

Ask for response_format: "url" on any media endpoint (see Embeddings, audio & realtime):

  • POST /v1/images/generations and POST /v1/images/edits — body field;
  • POST /v1/audio/music — body field;
  • GET /v1/videos/{id} — query parameter ?response_format=url.

The default stays b64_json, so existing clients see no change. Videos are billed exactly once and their MP4 is stored whichever format you poll with.

Every finished Studio render is stored as an asset too and appears under kind=render (GET /v1/assets?kind=render).

Sound effects imported from the free CC0 sound-effects library are assets of kind=sfx.

Errors

HTTPcodeWhen
400bad_requestInvalid kind, before (must be an ISO date-time) or limit.
404not_foundUnknown id, another user's asset, or /content on an asset without content.
503assets_unavailableAsset storage is not configured on this gateway.

SDKs

import { Codai } from 'codai-sdk';
const client = new Codai({ 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); // fresh 1 h URL
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 codai import Codai
client = Codai(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"])

Both SDKs also have list(...), retrieve(id), content(id) (downloads the bytes from the signed URL) and contentPath(id) / content_path(id) (the authenticated redirect URL).

On this page