The playground
The coding playground runs a coding agent on a private machine. The chat playground tries text models with browser tools, and the render playground generates images and video.
The coding playground, Studio and the chat playground's web tools need a browser that reports where a request came from: Chrome 76, Edge 79, Firefox 90, Safari 16.4, or later. An older browser is refused, because it cannot show that a request came from this site rather than from a page elsewhere.
Start a coding workspace
Sign in, open the coding playground, and choose Start a machine. Pick a size, a workspace, and an idle timeout. The start sheet shows the machine's price; inference is billed separately against your account. A larger machine gives your programs more CPU, memory and storage. It does not change the model's context window or the provider's inference capacity. The sheet offers only the sizes some machine host in the deployment can hold, so a size missing from it is one no host has room for rather than one that is busy.
The loading screen stays visible while the machine boots, the coding app connects, and the editor and workspace become usable. Starting with a saved workspace also waits for its files and coding state to restore. After the machine reports running, connection and app preparation share a three-minute deadline. If preparation fails, Try again reconnects to the same machine. Use the machine menu to shut it down if you do not want to keep waiting or paying for it.
Choose a model and harness in the coding workspace. Every machine offers seven harnesses: OpenCode, Claude Code, Codex, DeepSeek Harness, ZCode, Oh My Pi and Empryo. They run on the machine and use the marketplace for inference. Tell the agent what to build, review the files and tool results, and send follow-ups in its conversation. The chat playground described below has its own tools and settings.
Keep and restore coding work
Files under /workspace are copied into this browser while a tab is connected. The playground home
shows the workspaces this browser holds, their copy status, and which are on running machines.
Shutting down a machine destroys its disk. Start another machine with the saved workspace to restore
what the browser holds. A workspace already on a running machine cannot be assigned to a second one.
Keep the tab connected until the copy is current before shutting down. Changes made after the tab closes cannot reach the browser until it connects again. The machine can keep working while the browser is closed; if it shuts down first, those uncopied changes are lost.
Use Export to save a workspace zip and Import to create a workspace from one in another browser. A workspace's name and files stay in browser storage; signing in elsewhere does not fetch a server copy. Clearing site data deletes that browser's copy. The home page reports storage availability and whether the browser granted persistent storage.
The copy excludes Git metadata, generated files named by /workspace/.syncignore, and files larger
than 32 MiB. Conversation state under /workspace/.heimdall/state/ is included. A storage warning or
skipped-file count means the browser does not hold everything on the machine. See
data and privacy for the complete copy rules.
Open the coding preview
Ask the agent to start the project's server on 0.0.0.0:3000, then open Preview. A server on
localhost or another port cannot appear there. The harness receives instructions about that
address, file copying, and the disposable machine.
A restored workspace contains files, not running processes or system packages. Ask the agent to
install the project's dependencies and start its server again before opening the preview. Files
outside /workspace are not restored. Preview sharing is controlled from the machine's preview
menu; choose who can open the link before sharing it.
Stop a coding machine
The idle timeout stops a machine after the chosen interval without a message, run or edit from you or the agent. Closing the tab does not shut it down. A machine set to keep running continues to bill until you shut it down or credit runs out. Use the machine menu to shut it down explicitly, after checking that the browser copy is current.
Chat runs from your browser against the public API
Your browser calls POST /api/v1/chat/completions at the same base URL every code sample uses, with
an API key as the bearer token, and reads the stream back. Nothing sits between the tab and the API:
there is no private route, no server-side key, and no variant of the request an integration cannot
make. What works here works in code unchanged, and a request that fails here fails the same way from
a script.
Everything else follows from that. The conversation, the files the model writes and the key itself live in the browser, because the browser is the client.
A run belongs to the tab rather than to the page showing it. Browsing to the model list or the docs while a reply streams or a GPU renders leaves the work going, and coming back finds the thread where it got to with its stop control still in reach; the rail marks a thread that is still working, and several conversations run at once, each streaming into its own record. Closing the tab is what ends a run.
The key
Chat sends nothing until a key is chosen, and there are two ways to have one.
Create one from the page. Minting from the playground makes a real key on your account with a spend limit of $5. It bills its workspace like every other key, and it is listed and revocable under your account beside them.
Paste a key you already hold. A pasted key names no workspace on the page, because the plaintext is all the browser has of it.
Either way the plaintext is kept in that browser's local storage and nowhere else. Once stored, only the prefix is shown, and the API never reveals the rest again.
What the page tells you
Above the thread: the active key, the workspace it bills, the model, the conversation's session id,
how many files the thread holds, how full the model's context window was on the last reply, and what
the thread has cost so far. A thread in which nothing carried a price totals to an em dash, never to
$0.00.
The total opens on what it went on: the replies, the goal checks bought on them, and the folds, with
what the replies' subtasks and drawings came to indented under the replies row, because those are
billed to the reply that chose to spend them and are already inside its price. The total is what the
thread was charged and is never the sum of the rows beside it — every figure is rounded to the two
decimals it is shown to, so two calls of $0.014 read as a cent each next to a charge of three, and a
breakdown that had to add up would have to misstate one of the three. A row whose calls published no
price at all reads as an em dash, and a row short by an unknown amount carries a + the way the total
does.
Under each reply: the provider that served it and the price of every call the reply took, the tool
steps included. A reply whose calls published no price carries no figure at all. Nothing was
measured, so nothing is put on screen. A task or generate_image call carries its own price on its
card, because each of those spends on its own account inside the reply's figure.
Each conversation carries one
X-Session-Id across every request it makes, judge
calls and drawings included, so its turns prefer the endpoint that already served it and that
endpoint's prompt cache stays warm. A subtask is the one exception: it shares no prompt with the
thread that handed it off, so it carries a session of its own and the router is free to send it to
whichever endpoint has room.
The model and the settings
The picker searches the whole chat-capable catalogue by name or id. Each row shows the model's input modalities, its context window and its prices per million tokens, so the trade is visible before you commit. Switching models mid-conversation keeps the history and hands the next turn to the new model.
Every setting is optional, and anything left unset is omitted from the request so the serving endpoint's own default applies.
- System prompt, up to 8192 characters, sent as the system message.
- Temperature (0–2) and Top P (0–1), the sampling controls.
- Max tokens, the completion ceiling, which also bounds the credit hold.
On a model that reads images, attach up to 4 images per message, each at most 4 MB. A combined ceiling applies to the set as well, on its encoded size rather than on any one file, so four small images go where two of the largest do not; the composer names whichever limit refused an image. They ride along as base64 data-URL content parts, the same form the API accepts. Switching to a text-only model drops the attachments rather than failing the send.
The file tools
A conversation owns a file tree, and the model works in it through eight tools: write_file,
read_file, list_files, search_files, str_replace, create_dir, move_file and
delete_file. Nothing bounds how many files a tree holds or how large any of them is: the tree
is kept in the browser's own database, which holds what the browser's storage quota allows, so a site
of a dozen full-size renders is a site the tools will build. Paths are relative and at most 200
characters. A call the tree's shape cannot take — a file where a directory stands, a path with .. in
it — is refused in a sentence the model reads and acts on, rather than failing the reply.
A directory is an entry of its own. Writing src/app/page.tsx creates src and src/app along with
it, create_dir lays a folder out before anything is written into it, and a folder holding nothing
is part of what the archive saves. Deleting a directory that still holds entries needs recursive,
and the refusal says how many would go.
A file is either text or a run of bytes. Bytes are what a picture is, and the tree holds one as bytes
rather than as a string because a byte sequence that is not valid UTF-8 does not survive a decode.
Nothing bounds what one weighs: a rendered image's size follows from the envelope its
endpoint declares rather than from anything the caller writes.
list_files names a binary file's media type beside its size, read_file refuses one rather than
answering with the damage a decode would do, and search_files passes over it. Moving, deleting and
saving treat it as any other entry.
Reads and searches are bounded so that one call cannot spend a reply's worth of context. read_file
answers with at most 400 lines and 64 KiB, and takes a start_line and a line_count for
reading on from where it stopped; a read that is a range, or that either limit cut, opens with one
line naming the lines it answered and the file's own line count. search_files matches a literal,
case-sensitive substring across the tree's text files and answers path:line: text per matching
line, up to 100 matches, each line cut at 200 characters. It always says how many text files
it read and how many binary files it passed over, because a search that covered nothing and a search
that found nothing are different facts.
The file tools run in the browser tab and nowhere else. No server holds the tree, and no server executes what the model asks of it, which is what makes them safe to hand it.
They are offered only on models whose endpoints
declare tool calling, because a request carrying tools is routed
nowhere else. A reply that uses them is several requests rather than one, and the loop has no fixed
ceiling: it runs until the model answers in prose or the stop control ends it, with every call priced
into the running cost on screen and bounded by the key's own spend limit. Every call renders in the
transcript as a card carrying its name, its arguments and its result.
Reading the web
Two tools read the public web. fetch_url takes one URL and answers with the page's readable text
and the links on it rather than its markup, so what enters the thread is what a reader would see and
not a document's worth of tags. Only http and https are followed and only addresses
on the public internet are read. One fetch has 15 seconds, follows at most 4 redirects, and
refuses a response that is not text rather than describing it. Length is a bound rather than a
refusal: a document stops arriving at 20,000,000 bytes and its text is cut at 20,000
characters, and the answer says which cut it made. The beginning of a long page is usually the part
worth having, which is why a front page running to megabytes is read rather than turned away.
web_search answers with up to 8 results, each a title, a URL and, where the engine gives one, a snippet. A query
is its only argument. There is no next page to ask for and no way to ask for more, so a query that
does not surface what you want in eight results is one to rewrite. It is offered where the deployment
configures a search backend and is simply absent where none is configured, rather than present and
answering that it is unavailable.
A search is answered against the engines that answered it. The backend queries several at once, and one can drop out on its own for a rate limit, a timeout, or a CAPTCHA served to the address the search goes out from. Where that happens the result names each engine and the state it reported, so a short list reads as short coverage rather than as the web's verdict. A search that came back with nothing at all beside an engine that did not answer is an error naming those engines rather than a page saying nothing was found. Nothing was asked of them, so the empty answer is not a fact about the web.
These two run through this site rather than in the tab, because a browser can neither fetch across origins nor check where a name resolves to. The request carries the URL or the query and nothing else of the conversation. It is a signed-in call on this origin, so it carries your session cookie the way every other playground request does; what it does not carry is your key, your files, or anything else said in the thread. The page itself is then read from this site with no cookie, no credential and no header naming the marketplace. What makes that safe is the address rule. The check is on the address actually connected to rather than on the name asked for, at every hop, so a name that resolves inward is refused where a name-only check would follow it.
Drawing a picture
generate_image draws one image from a text prompt and writes it into the tree as a PNG. It posts to
POST /api/v1/images at the same base URL the conversation's completions use, with the
same key as the bearer token and the same session id, so a drawing is an ordinary API request billed
to that key. What it cost joins the price shown under the reply that chose to spend it, beside the
completions that reply paid for, and its own card names it so the reply's figure can be read for how
much of it was drawing. A generation the API priced at nothing published no price, so the reply says
the cost is not known rather than naming zero. A call refused before any request reached the API —
a size the chosen model does not render — was charged nothing and carries no figure at all, which is
a different fact from a price nobody published.
One call draws one image, because one call writes one path. model offers the catalogue's image
models and size offers every resolution those models render between them, since a request naming
any other is routed nowhere; a size the chosen model does not render is refused before anything is
drawn or charged. seed fixes the render, so the same prompt at the same seed redraws
the same image.
The PNG lands in the tree at whatever it weighs: the smallest square a listed image model draws is 512×512, and a PNG of that square with photographic detail is around 0.7 MB. The write is refused only by what the tree's shape cannot take, such as a path with a directory already standing at it, and the refusal says the image can be written to another path without drawing it again. The image was drawn and charged either way, because the render happened before the tree turned it down.
The tool is offered where the catalogue lists an image model a live endpoint renders a size for, and is absent otherwise rather than present and answering that it cannot draw. The render playground is the other way to render, and it bills differently: it runs on your session rather than on a key.
Running a snippet
run_js evaluates a piece of JavaScript and answers with what it evaluated to and what it printed.
It is for checking a calculation, a regular expression or a piece of logic before it goes into a
file, rather than for running the project: one expression answers with its value, a block of
statements answers with what it returns, and top-level await works.
It runs in a sandboxed frame with no DOM of yours and no origin of ours: it cannot read the page in
the preview, reach this site, or touch the tree. The frame is granted scripts and never
allow-same-origin, so the origin it runs from is nobody's, and a request to this site carries no
cookie because there is no same origin to carry one to. That is the bound, rather than a list of
names hidden on a global object: a name shadowed on the global is still on its prototype, and a
nested realm gets every one of them back.
A snippet still running after 3 seconds is ended and its frame taken out of the page, so a loop that never exits costs one call rather than the tab. Console output is captured, at most 100 lines with each line and the returned value cut at 1000 characters, and the answer says how many lines the bound shed. A snippet that throws answers with the error, because what a snippet got wrong is the part worth reading.
Seeing the site
Once the tree holds an index.html, the Preview tab renders what the files add up to. Nothing
serves the tree and an unresolved reference has no address to fetch, so every reference is resolved
into one document. A stylesheet becomes a <style>, a script becomes the program it names, and the
references a browser would fetch become data URLs: link href, img src and
srcset, source src and srcset, audio src, video src and poster, iframe src, object data, embed src, track src, the src of an input type="image", and the href or xlink:href
of an SVG use or image. A binary file goes in as its own bytes under its own media type, so a PNG
the model drew renders as the picture it is; a file written without a type of its own is served as its
extension names it, the way a static host serves it.
The composition follows those references onward through the files they name. A stylesheet's url()
and @import, and a module's import specifiers, resolve against that file's own directory rather
than the document's, the way a browser resolves them against the address it fetched the file from. A
bare specifier such as import 'three' names something the tree does not hold, and is left as
written for the browser to answer.
Two lists sit under the frame, because they are two different things to fix. A reference naming a
file the tree does not hold is listed by name, so a stylesheet that never loads reads as a file never
written. A file the tree does hold and the composition could not carry is listed separately with its
reason: a cycle among modules or @imports, a run of bytes where a script or a stylesheet is text,
or a document that would pass 32 MiB. That last figure is memory rather than transfer: the composed
document is one string the panel holds while the frame parses its own copy, and binaries reach it as
base64 at four characters per three bytes, so 32 MiB carries about seventeen 1.4 MB renders beside a
site's own source.
The page runs whenever the conversation holds an index.html, whether or not the Preview tab is
showing and whether or not that conversation is the one on screen, because a loop that writes a page
and then reads what it printed has nothing to read otherwise. Every thread that is working runs its
own page in its own frame, so two conversations building sites at once each read their own console. A
thread that has finished and is not on screen runs nothing, and a read of its console reports its last
run. What a page prints and what it throws is captured from the document's first line:
console.log, info, warn, error and debug, uncaught exceptions and rejected promises, each
with the file and position the browser named. They appear under the frame, and the model reads the
same run through read_preview_console, which takes an errors_only argument for the faults alone.
One run keeps its newest 200 records, each cut at 2000 characters, and the answer says how many it shed. A run that reported nothing says so, which is a different fact from no page having run: an empty console is a quiet run only where something was watching. A read made straight after a write waits up to 3 seconds for the page to run again, so what it reports is the page that write produced rather than the one before it.
The frame is sandboxed with allow-scripts and never allow-same-origin. Granting both together
would lift the sandbox and make the model's markup a page on this origin, with this origin's storage
and the key in it. Opening the preview in a tab carries the same frame around it, for the same reason.
Keeping the files
A file saves as itself. The tree saves as one zip with its paths intact, because
assets/css/main.css beside index.html is what makes the stylesheet resolve once the folder is
open, and a folder holding nothing is an entry of the archive so the layout unpacks whole. Saving
moves bytes and runs nothing.
Goal mode
Goal sits by the composer and is off by default. Off is a thread that sends exactly what you type.
Turned on, each message becomes a goal. When a reply ends its tool loop and answers in prose, it is offering that answer as finished, and a second model call reads it against your message to decide whether it is.
That check is handed three things to start from: the messages you wrote, which are the goal; the reply's final text; and an inventory of the tree with each file's size. The three are cut at 4000, 4000 and 2000 characters, and a section over its bound says how much it left out rather than stopping mid-sentence. The inventory is a map of the tree rather than evidence about what is in it, because a file listed at a plausible size can hold anything at all.
So the check reads the work. It holds a read-only registry over the same tree the reply wrote, the
console of the page that tree composes to, and the log of the calls the reply made: read_file,
list_files, search_files, read_preview_console and read_tool_log. It gathers what it needs
and then decides, so a reply claiming a file does something is a claim the check settles by opening
that file. It writes nothing and starts no run of the page; what it reads is what the reply left
behind.
A supervisor with tools is a second loop, so it runs under a budget: 12 evidence calls and 65536 bytes of answers across them, with a call the budget refuses counting as one of the 12. Past either bound every tool answers that the budget is spent and nothing else. The numbers are in the check's own prompt, so what it is told it may spend and what it is allowed to spend are the same numbers. What a check read travels with its verdict and is named beside it in the transcript, so a check the budget stopped says what it did not get to instead of offering a partial reading as a whole one.
The check answers through a single verdict tool, demanded with
tool_choice: "required". The marketplace holds a demanded
tool call as a contract: an attempt that answers without one fails over to another endpoint, and an
exhausted attempt errors. So the check either carries a verdict or costs a request and says so. There
is no fabricated verdict standing in for one, because a check reporting "met" on a call that never
happened ends the goal on a claim nobody made.
When work is outstanding, the check names it in one sentence and that sentence goes back to the model as the next message, marked in the transcript as the supervisor's rather than as yours. One goal is sent back at most 3 times; after that the outstanding work is reported and the thread is yours again. A reply an endpoint stopped at its output ceiling never claimed to be finished, so it is continued rather than checked, and no check is paid for to be told what the reply already shows.
Each check is an ordinary request on the same key at the same listed price, and each marker names
what its own check cost across every call it took, reading included. A check billed at a price nobody
published shows an em dash there, a check one of whose calls published none carries a +, and a
check the API never answered names the failure instead of a price.
Subtasks
Beside the file tools, a reply can call task: it hands one self-contained subtask to a fresh agent
and waits for the answer. The subagent is given the prompt and nothing else of the conversation,
works in the same tree, reads the web and draws with the same tools, and answers in text, which is
the whole of what comes back. Its own tool calls stay out of this transcript. It has no task tool
of its own, so it cannot delegate further, and its loop ends the way a reply's does: when it answers,
when the stop control ends the send, or when the key can no longer pay.
Its calls are billed to the same key as every other call the reply makes and counted in the price shown under that reply. Its card names what the subagent's own completions came to, so the reply's figure can be read for how much of it was delegation; a picture the subagent drew is counted as drawing rather than as delegating, so the two shares of a reply never overlap and neither leaves a charge out. While a subtask runs, its card carries what it was asked to do and how long it has been running.
Asking you a question
A reply can call ask_user and stop there. The question appears under the transcript with the answers
it offered and one sentence saying what turns on it. Pick one or write your own, and what you write
becomes that call's result, so the model reads it where it asked rather than as a new turn.
It is for a fork the work turns on: two or more courses that lead somewhere genuinely different, where nothing in the conversation, the tree or a tool decides between them. It is not for permission to carry on, not for approval of a decision already taken, and not for anything a tool could find out. A question offers between 2 and 6 answers and carries at least 40 characters saying why it cannot be settled without asking, which is what keeps asking from being cheaper than deciding.
A thread suspended on a question is suspended rather than running, and spends nothing while it waits. It survives a reload, because the question lives in the reply that asked it and there is no stream to rejoin. The stop control ends it and records the call as one the person declined to answer, so the thread does not carry an unanswered call into every request after it. Your other conversations keep working meanwhile.
Conversations
Conversations are kept in that browser's own database, up to 60 of them, and the oldest is shed when the history reaches that depth. Each carries its messages, its files, its settings and its session id. A browser that runs out of room says so under the history rather than dropping threads to make the write fit.
Replies stream, and stopping one keeps what has arrived. Edit and resend rewrites the last user message and runs the exchange again; retry reruns it unchanged, which is how the same prompt gets compared across two models or two temperatures.
A send belongs to the conversation it was made in, so several threads work at once. Opening another conversation does not stop the one you left: its reply keeps arriving, the sidebar marks it as still working, and the stop control ends the thread it sits under and no other. Everything above the transcript — the session id, the context meter, the running cost — describes the conversation on screen. Closing the tab ends every send in flight, because the requests are made from this page and nothing on the server is holding one open for you.
Long conversations
The conversation this browser keeps and the conversation a request carries are two different things. Every message stays here for as long as the thread does. Past a point, what goes on the wire is a summary of the early work followed by everything since it. Three things reach that point.
A measured threshold. The strip reports how full the window is as ctx, taken from the prompt
the endpoint counted on the last reply rather than from a guess made in the browser: the count comes
from the tokenizer that will bill the next request. At 70% of the model's declared context the thread folds
before the next message goes. A send an endpoint turns down as too long counts the thread too, and the
meter reads the window and the prompt out of the refusal's own numbers, measured against the smaller
of that window and the declared one. A thread with neither a counted reply nor a refusal shows an em
dash there and folds on nothing, because an uncounted window is not an empty one. A fold spends the
refusal that measured what it replaced: those numbers counted the messages the summary now stands in for.
The Compact control, beside the meter, which folds the thread on demand from the latest reply.
A refusal on size. When the endpoint turns a send down as too long, the thread folds and the send goes again, once. A second refusal reaches you instead of folding anything, since a single message larger than the window is not something a summary can fix.
A fold is one ordinary request, on the same key and the same model, held to a single step with no tools. It is shown the conversation being folded, with each tool result cut to its name, what it acted on and the size of what came back. The tree is still open in the panel beside the thread, so the file bytes are not worth saying twice. The goal, what has been decided and what has been tried cannot be read off the tree at all, and those are what the summary is asked for.
The line a fold draws in the transcript names what it cost, and the thread total above counts it. Opening that line shows the summary the request now carries. A fold the API answered without producing a summary still names its price, because the request was billed either way.
A thread carries one summary, always the latest. Folding again folds the previous summary in with the work since it and replaces it, so what is sent never grows a chain of them.
Nothing is deleted. Scrolling back reaches every message the thread ever held, on either side of a fold.
Replies that run out of room
An endpoint can stop a reply at its own output ceiling rather than at an ending the model chose, which leaves the text stopping mid-sentence with the work neither done nor abandoned. The playground sends the reply straight back to work, and the transcript carries a line saying it did so.
Nothing caps how many times. Each continuation is an ordinary billed request, and what bounds it is what bounds the tool loop: the running cost on the strip, the stop control, and the key's own spend limit.
Replies the provider stops answering
An endpoint can stop answering after the reply has started arriving, which leaves the thread holding half a reply and often half a tool call. The playground sends that turn again, once: the second attempt is a fresh request, routed afresh rather than replayed against the same failure, and the half-reply it replaces goes off the wire with it. A second failure on the same turn reaches you instead, because a thread that resends forever is a thread spending forever.
What the playground will not do
- The tools run in the tab, with four exceptions. The file tools execute in the browser, and
run_jsand the preview each render in a sandboxed frame that cannot reach this origin.fetch_urlandweb_searchrun through this site, because a browser can neither fetch across origins nor check where a name resolves to, and they carry a URL or a query and nothing else of the conversation.generate_imageandtaskgo straight to the public API on the conversation's own key, the way every other call that reply makes does. - Nothing is uploaded. The conversation, the files and the key are in the browser. There is no link that shares one and no copy to recover if the browser's storage is cleared, so save what you want to keep.
- Nothing on the server is a transcript. The generation records behind these requests carry ids, token counts, timings and cost, which is what settling a request needs and not what it said.
Generating images and video
Rendering gets a page of its own as well as the chat tool, because the two are for different work. Nothing streams — a render arrives whole after the better part of a minute — and what you do next on this page is rarely "say something else" but "same prompt, different seed" or "same seed, larger", so it is a set of controls beside a sheet of results rather than a thread.
Stills and clips share the rendering page. Frame geometry limits what can be generated; the
endpoint’s declared image or video tariff determines the charge. Pick a model and the controls follow what its endpoints declare: a model whose
endpoints publish a video_envelope gets a length and a frame rate, one that publishes an
image_envelope gets a count of pictures, and a model whose endpoints publish both lets you choose
which you are asking for.
The controls show the request’s geometry and available pricing information:
- the Render button shows a quote only where the declared tariff and selected settings support one. An unfinished ask, unsupported tariff or unknown quantity does not display a free price;
- every control offers what a live endpoint holds and takes a free value beside it. The size control offers each framing at the largest size an envelope holds it at, and the length control offers only clip lengths that land on the endpoint's frame grid. Type your own and an ask outside every envelope is answered on the spot with the bound that turns it away and the values that clear it, so a request routed nowhere costs you a sentence rather than a wait;
- while a render is in flight the card counts seconds against that model's own measured response time, so the wait has a shape.
The Provider control beside the model names who serves it, for images, clips and 3D meshes
alike. Auto leaves the choice to routing, and the controls cover every live endpoint of the
model. Each provider serving the model is listed with the prices its cheapest endpoint charges in the
rail's own unit, read "from" where its other endpoints charge more. Choosing one sends the request
with provider: { only: [slug] } and narrows every control to what that provider's endpoints
declare — sizes, lengths, frame rates, face range, texture, reference roles and parameters — and the
listed rates become that provider's. When the provider changes, what you set is kept whole where one
of its endpoints serves it all. Otherwise each value is judged on its own and one the provider does
not accept is put back on what it offers; values each accepted alone that no single endpoint serves
together are then put back in a fixed order — length, faces, texture, size — until one does. A
provider that stops serving the model falls back to Auto, and rendering a past result again at a
provider that has left is refused with a sentence saying so. A model one provider serves
names that provider without offering a choice. Adapters are served by the provider holding their
weights, so selecting any pins the request to that provider and narrows the controls the same way.
Nothing is rounded onto a grid for you, on either axis. Four seconds at 25 fps asks for 100 frames, and an endpoint on an 8n+1 grid renders 97: the control says so and names both lengths that land rather than quietly shortening your clip. The frame count sits under the length to explain the engine’s duration grid. The price follows the declared video tariff.
Each result carries its size, its frames and rate where it has them, and its seed, with reuse (copy the seed into the settings), vary (the same prompt at the next seed) and save. Clips play on arrival, muted, with controls to turn a soundtrack back on. Generations are kept in that browser only, because the results are megabytes each and the marketplace stores none of them.
What stops a message
Every chat call is billed to the key's workspace at the serving provider's listed price, exactly
as an API request would be. A message stops when either budget above it runs out: the account's
spendable credit, or the key's own spend limit, which for a key minted here is $5. A render from the
render playground carries no key: it bills your account's default workspace, and only the account's
credit bounds it. A generate_image call from chat carries the conversation's key like every other
call that reply makes, so both budgets bound it. The reply carries the API's error either way. Top up
under Deposits, or raise the key's limit from your account.
Chat carries your API key, so it falls under that key's own rate-limit bucket like any other API traffic — see errors & rate limits. The render playground runs on your session instead, with its own allowance on each rail: 6 images a minute and 1 clip a minute. A still render holds a GPU for the better part of a minute where a completion holds one for a second, and a clip holds one for minutes — measured, a mid-sized 720p clip takes 132 seconds and the largest an envelope admits takes 280 — which is why each is metered on its own.
From playground to code
Chat sends an ordinary request: model, the messages, the optional system, temperature, top_p
and max_tokens, and tools where the model's endpoints declare them. A request carrying tools
also carries a short harness note ahead of your own system prompt, grouping the tools by what they
are for and saying when each group is worth a step. A group is in the note only where its tools are in
the request, so a deployment with no search backend or a catalogue with no image model produces a
shorter one. Nothing else is added to what you send.
Lifting a working configuration into an integration is carrying those fields across. The routing
controls the API also offers — provider preferences, fallbacks — are described in
Models & routing.