API reference
Generate images and video from your own application. The API is server-to-server: your backend holds the key and calls us, the same way you would use any paid generation API. Work is billed per asset against a prepaid credit balance.
Authentication
Send your key as a bearer token. Keys look like cvk_<prefix>_<secret> and are shown once, at creation.
curl https://corovusagent.com/api/v1/media/images \
-H 'Authorization: Bearer cvk_a1b2c3d4_…' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 9f2c1e40-…' \
-d '{ "prompt": "a ceramic mug on a linen cloth", "quality": "standard" }'Never put a key in browser JavaScript or a mobile binary. It is a full credential — whoever holds the string can spend your balance. There is no CORS on this API by design; calls must come from your server.
Idempotency-Key is required on /images. Send a unique value per submit; retrying with the same value returns the original result instead of generating — and paying — twice.
A successful call answers:
{
"ok": true,
"id": "8f1c2d3e-...",
"request_id": "3ab2...",
"status": "succeeded",
"image": {
"url": "https://media.13idea.org/mediaapi/.../0.png",
"content_type": "image/png",
"aspect_ratio": "1:1"
},
"credits_charged": 103,
"balance_credits": 897
}Base URL
https://corovusagent.com/api/v1/media
The API is host-neutral, so the console host answers identically. Either works with the same key.
Synchronous images, asynchronous video
Image generation is synchronous. POST /images holds the connection and returns 200 with the finished image — typically 10–30 seconds, and up to about five minutes in the worst case. Set your HTTP client timeout to at least 300 seconds, and send an Idempotency-Key on every submit so a timeout is always safe to retry.
Video is different. Renders take minutes, far longer than any load balancer will hold a connection open, so POST /videos is asynchronous: it returns 202 with an id and a poll_url, and you poll GET /jobs/{id} until its status leaves processing. Credits are held at submit and only charged when a video is delivered; every failure is refunded. The expires_at on the 202 is the point after which the job is definitively dead and the hold released.
Endpoints
/imagesGenerate one image from a text prompt. You choose a quality tier and an aspect ratio; we choose the engine. Synchronous — returns the finished image./videosText-to-video, image-to-video and video-to-video. Asynchronous — returns 202 with an id to poll./jobs/{job_id}Look up one submit by its id — status, credits charged and the output URL. Use it to recover a result whose connection dropped. Video jobs will use the same endpoint./uploadsnot yet liveGet a presigned URL to upload your own source image or video before referencing it./pricesnot yet liveLive price list in credits, so you can budget before you call./menot yet liveBalance, quota, rate limit and enabled capabilities.POST /images — parameters
Text-to-image only for now. Unknown fields are rejected with a 400 rather than ignored, so a parameter that is not listed here will never be silently dropped from a call you were billed for.
| Field | Type | Notes |
|---|---|---|
| prompt | string | Required. 1–2000 characters. |
| quality | standard | premium | Optional, defaults to standard. The premium tier uses the higher-fidelity engine and costs more. |
| aspect_ratio | 1:1 · 4:5 · 5:4 · 3:4 · 4:3 · 2:3 · 3:2 · 9:16 · 16:9 · 21:9 | Optional, defaults to 1:1. |
POST /videos — parameters
Pick a mode; the fields below depend on it. Source media is sent inline as base64 — there is no URL field on any mode, and never will be. The whole request body is capped at 16 MB, which is roughly 12 MB of source once encoded, and a source clip may not exceed 15 seconds. Downscale before sending.
| Mode | Fields | Notes |
|---|---|---|
| text_to_video | prompt, seconds, quality, aspect_ratio | seconds 4–15 (default 5). quality standard (720p) or hd (1080p). |
| image_to_video | prompt, image_b64, seconds, quality, aspect_ratio | Animates a still. Same duration and quality options. |
| video_to_video | prompt, video_b64, aspect_ratio | Restyles a clip. NO seconds field — the duration is measured from your file, and that measured length is what you are billed for. |
Real people need consent. If your source shows an identifiable person, the call is refused with likeness_consent_required until you resend with likeness_consent: true. Sending it records an attestation against your account that you hold that person's documented consent. Do not send it otherwise.
Sources are stored on our CDN only while the render runs — they are publicly readable at an unguessable path for that period, because the render engine fetches them — and are deleted as soon as the job finishes. Do not send source material that cannot be public for a few minutes.
Video has no audio. Every clip is delivered as a silent H.264 mp4 with no audio track. There is no audio parameter, and the per-second price covers video only. Plan for your own soundtrack if you need one.
Output storage
Generated images are stored on our CDN and returned as a direct URL. That URL is unguessable, but it is public and permanent — anyone holding the link can fetch the image, and it does not expire.
Download and re-host anything that needs to stay private or that you need to guarantee remains available.
Errors
Every failure returns a machine-readable code — branch on that, never on the message text, which may be reworded.
| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_request | Body failed validation, carried an unrecognised field, or the Idempotency-Key header was missing. |
| 401 | unauthorized | Missing, malformed, revoked or expired key. |
| 403 | forbidden | Account not active, or this key lacks that capability. |
| 403 | scope_paused | That capability is temporarily paused on your account. |
| 402 | insufficient_balance | Not enough credits for this call. |
| 402 | monthly_cap_reached | Your period cap is exhausted. |
| 409 | idempotency_key_in_flight | A request with this Idempotency-Key is still running. Retry once it completes. |
| 413 | source_too_large | Request body exceeds 64 KB. |
| 429 | rate_limited | Slow down. Honour the Retry-After header. |
| 422 | content_blocked | The prompt was refused by content screening — ours or the engine's. You are not charged either way. |
| 503 | vendor_unavailable | An upstream engine is down. Retry with backoff. You are not charged. |
| 504 | timeout | Generation took too long. You are not charged. |
Rate limits & retries
Responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, and a 429 carries Retry-After in seconds. Limits are weighted: a generate call costs more of your budget than a job poll, so polling will not push you into a limit.
Send an Idempotency-Key on every submit — it is required on /images. If a response is lost and you retry with the same key, you get the original result back instead of paying twice. A retry that arrives while the first call is still running answers 409 idempotency_key_in_flight.
Request ids
Every response includes X-Request-Id. Quote it when you report a problem — it is the same id shown in your Usage tab and in our logs, so it turns a support thread into one lookup.