Working with
the render route
One route handles every model and both output types. Rendering is asynchronous — you get a job id back before the file exists, then poll or wait for a webhook.
01 Quickstart
Connect a wallet on Robinhood Chain and you have an account. There is no signup, no email, and no API key to generate — requests are authenticated by a signature from your address.
// POST /api/render { "prompt": "a kestrel mid-downstroke, backlit", "model": "standard", "ratio": "16:9" } // 202 Accepted { "id": "rnd_8f2c41", "state": "queued", "cost": 2, "balance": 10 }
Then poll GET /api/render/rnd_8f2c41 until state is terminal. On the fast models that is usually two or three polls.
02 Credits
A credit is the unit you spend on a render. Every address gets 2 free credits per calendar month, reset on the first. Unused credits do not carry over.
Cost is charged when the job is accepted, not when it completes, and refunded automatically if it fails. That ordering is deliberate: charging on completion would let one address queue unlimited concurrent jobs against a balance it does not have.
Your balance drops the moment you send the request. If you cancel a job mid-render the credits come back, minus nothing — cancellation is free.
03 The render route
Every model goes through POST /api/render. Changing engines means changing one string, not rewriting your integration.
| Method | Path | Does |
|---|---|---|
| POST | /api/render | Start a job. Returns 202 with a job id. |
| GET | /api/render/:id | Read one job, including its file URL once ready. |
| DELETE | /api/render/:id | Cancel a queued or rendering job. Credits refunded. |
| PATCH | /api/render/:id | Set visibility to public or private. |
| GET | /api/renders | List your jobs. Paginated, newest first. |
| GET | /api/explore | The public feed. No wallet needed. |
04 Parameters
Everything except prompt has a default. Send only what you want to change.
| Field | Accepts | Notes |
|---|---|---|
| prompt | string, 1–1200 chars | Required. Over the limit returns 400 — nothing is silently truncated. |
| model | turbo · standard · fidelity | Defaults to standard. |
| ratio | 1:1 · 16:9 · 9:16 · 4:3 · 3:2 | Defaults to 1:1. |
| output_format | jpeg · png | Defaults to jpeg. PNG is lossless and costs the same — use it when the image contains text. |
| type | image · video | Defaults to image. See the video flag below. |
| seed | integer | Reuse a seed with the same prompt and model for a near-identical render. Omit for random. |
| visibility | private · public | Public renders appear at /r/:id and in Explore. Defaults to private. |
05 Job states
A job only moves forward. It never returns to an earlier state, so you can stop polling the moment you see a terminal one.
| State | Terminal | Meaning |
|---|---|---|
| queued | no | Accepted and paid for. Waiting on capacity. |
| rendering | no | Sent to the engine. |
| ready | yes | url is populated and the file is stored against your address. |
| failed | yes | error says what went wrong in plain language. |
| cancelled | yes | You called DELETE before it finished. |
| refunded | yes | Credits returned. Follows every failed and cancelled job automatically. |
06 Webhooks
Rendering is driven by a webhook from the engine, not by a background worker. The engine signs its callback with Ed25519 and the route verifies that signature before it reads a single field.
// the job object you poll { "id": "rnd_8f2c41", "state": "ready", "url": "https://…supabase.co/storage/v1/object/public/renders/…", "model": "standard", "seed": 418207, "cost": 2 }
The engine's own file URLs expire. Every finished render is copied into our storage bucket and it is that copy you get back — a link you saved a month ago still works.
07 Models
| Model | Cost | Output | Use it for |
|---|---|---|---|
| turbo | 1 | 1024px image, ~4s | Fastest route. Good for iterating on a prompt before committing. |
| standard | 2 | 1536px image, ~8s | Balanced quality and cost. The default for most renders. |
| fidelity | 4 | 2048px image, ~18s | Highest detail and prompt adherence. Slower, worth it for finals. |
| motion | 8 | 5s clip, 1080p | Locked until launch. |
| motionpro | 16 | 8s clip, 1080p | Locked until launch. |
There is no separate model for images with text in them. Set output_format to png instead: the engine is the same, the cost is the same, and the lettering survives because nothing is thrown away in the encoding.
08 Video flag
Video generation is complete. The route accepts it, the queue handles it, the gallery displays it. A single flag keeps it shut until token launch.
While the flag is off, type: "video" returns 423 Locked — not 404. The capability exists; it is withheld. Your integration can send video requests today and start working the day the flag flips, without a deploy on your side.
The flag has to be read server-side on every request. Hiding the tab in the interface is not a lock — anyone with devtools can post type: "video" straight at the route.
Video also carries a daily cap while it is subsidised. Once the day's clips are used up the route answers daily_cap_reached and image renders carry on untouched. The day rolls over at midnight Jakarta time.
Credits held when the flag flips spend on video at the published rate. Nothing expires and nothing is reset at launch.
09 Errors
Errors are plain objects with a machine-readable code and a sentence you can show a person without rewriting it.
| Status | Code | Means |
|---|---|---|
| 400 | prompt_too_long | Over 1200 characters. Nothing was charged. |
| 400 | unknown_model | The model string does not match any route. |
| 402 | insufficient_credits | Balance is below the job cost. Includes needed and balance. |
| 423 | video_locked | Video is built but switched off. Try again after launch. |
| 429 | daily_cap_reached | The day's video budget is spent. Image renders are unaffected. |
| 429 | rate_limited | Includes retry_after in seconds. Wait and resend. |
| 500 | engine_failed | The engine returned nothing. Credits already refunded. |
| 502 | upstream_failed | The engine would not accept the job. Credits already refunded. |
10 Limits
Limits keep the free tier free. They are per address and enforced server-side.
| Limit | Value |
|---|---|
| concurrent | 2 jobs per address |
| rate | 20 requests per minute per address |
| prompt | 1200 characters |
| storage | Renders kept 90 days on the free tier |
| new address | First render available immediately. No waiting period, no minimum balance. |
A brand-new wallet is a first-class case, not an edge case. Most people arrive with one.