How to Chat with Google Flow Agent via the Google Flow API
8 min read • October 17, 2026
Table of contents
- Introduction
- Before you start
- How a chat works
- Step 1: Start a chat
- Step 2: Stream the reply
- Step 3: Approve spending
- Step 4: Get the media
- Step 5: Continue or revise
- Step 6: Read, list, rename and delete chats
- Step 7: Settings for approval, instructions and default models
- The same from an AI assistant
- Examples
- Frequently asked questions
- Conclusion
Introduction
The useapi.net Google Flow API lets your code chat with Google Flow Agent, the creative assistant built into Google Flow: start a chat with one curl, stream its replies, approve what it wants to spend and download the images and videos it makes. Google offers Flow Agent only inside the Flow web app, with no public API (as of October 2026). useapi.net is a third-party API, not affiliated with Google, and the chat runs on your own Google account. Chatting with the agent costs no credits, and the images and videos it makes cost the usual Flow price.
This guide is one real run, start to finish. We asked the agent for two product shots of a ceramic coffee mug and a 6-second video of it, approved the spend, collected the media, then asked for a revision and rejected that one. Every response below is from that run, trimmed and with the account’s email replaced.
The agent plans the work itself. It picks the tools and models, guided by the project’s settings, and it keeps the whole conversation on Google’s side, so a chat can go on hours or days later with full context. The Agent Overview covers every rule and limit.
Before you start
You need a useapi.net API token and a Google account connected through Setup Google Flow. The agent works on every Google AI plan, including a free account, and what it can generate follows the account’s plan, the same as POST /images and POST /videos.
Each turn uses one captcha, solved by the API with your captcha providers or the free captcha credits of your first account. You do not send anything for it.
The steps use curl, jq and three shell variables:
export USEAPI_TOKEN="user:12345-..." # your useapi.net API token
export EMAIL="[email protected]" # a connected Google Flow account
API="https://api.useapi.net/v1/google-flow/agent"
Every call below is also in the Postman collection.
How a chat works
A turn is one message from you and everything the agent does in reply. A chat runs one turn at a time:
- POST /agent/chats starts a chat and answers
202with achatId. - GET /agent/chats/
chatId/stream follows the agent live over Server-Sent Events. - When the agent wants to spend credits, it stops and asks. POST /agent/chats/
chatId/approval answers. - POST /agent/chats/
chatId/messages sends the next message. - GET /agent/chats/
chatIdreads the whole chat, with fresh media links.
Step 1: Start a chat
Send the brief as message. The call returns as soon as Google has accepted the turn, which usually takes a few seconds and can take up to about 100 seconds, so give curl a 150-second timeout:
curl -s --max-time 150 -X POST "$API/chats" \
-H "Authorization: Bearer $USEAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "'"$EMAIL"'",
"message": "Create two product shots of a matte sage-green ceramic coffee mug on a light oak table in soft morning window light, square format, with Nano Banana 2.1. Then make one 6 second video of the same mug with steam slowly rising from the coffee, with Omni 1.1 Flash."
}'
Ours answered 202 in about 10 seconds:
{
"chatId": "c-u12345-email:[email protected]:74262ac8-a54f-4ad7-80d7-3d44a4d6c6c1-chat:55023a0f-5e70-4545-bb4a-d878038dcf35-bot:google-flow",
"email": "[email protected]",
"turn": 1,
"status": "running"
}
The chatId names the account and the project, so later calls need neither. It contains : and @, so URL-encode it in the path. Keep both forms:
CHATID="c-u12345-email:[email protected]:74262ac8-...-bot:google-flow"
CHAT=$(jq -rn --arg v "$CHATID" '$v|@uri')
Without projectId, the chat lives in the account’s own agent project, created on its first chat and named like My Agents Work Fri, Oct 16, which keeps agent work apart from your other Flow media. You can also attach up to 7 images, a video, 5 voices and 7 characters to any message with the same @-mention syntax as POST /videos, see POST /agent/chats.
Step 2: Stream the reply
Open the stream with -N so events print as they arrive. The browser’s EventSource cannot send the Authorization header, so use fetch() or any HTTP client in an app:
curl -s -N "$API/chats/$CHAT/stream" \
-H "Authorization: Bearer $USEAPI_TOKEN"
A new stream first sends a chat event, replays the chat’s history, then sends replayed. Everything after replayed is live. Here the first turn had already ended when we connected, so it came in the replay. The agent stated its plan, then asked for permission to spend 10 credits on 2 images and 1 video (ui and other long fields left out):
event: chat
data: {"chatId":"c-u12345-email:[email protected]:74262ac8-...-bot:google-flow","status":"waiting_approval","turns":1}
id: 1:505e78d7-bd79-4938-b0ec-8117bdb933dc
event: message
data: {"id":"1:505e78d7-...","turn":1,"items":[{"kind":"text","text":"I'm going to generate two images of a matte sage-green ceramic coffee mug, then I'll create a 6-second video of the same mug with rising steam using Omni 1.1 Flash."}]}
id: 1:00c21a03-a687-4173-9fd9-190fcdf2447b
event: message
data: {"id":"1:00c21a03-...","turn":1,"items":[{"kind":"tool_call","tool":"open_chat_panel","args":{"_synthetic":true}},{"kind":"tool_call","tool":"ask_for_permission","args":{"num_total":3,"total_cost":10,"num_videos":1,"num_images":2}}]}
id: 1:approval
event: approval
data: {"turn":1,"cost":10,"images":2,"videos":1,"total":3}
event: replayed
data: {"turns":1}
The events you act on:
| Event | What it means |
|---|---|
message | One complete message of the agent: its text, thinking, tool calls and tool results |
approval | The agent asks before it spends cost Flow credits |
turn | A turn ended: completed, waiting_approval or failed with an error |
media | An image or video the agent made, or a change of its status, with its url once completed |
A keepalive comes every 15 seconds. One stream covers the whole chat across turns, so leave it open while you approve and send messages. If it drops, reconnect with the last id in the Last-Event-ID header and it picks up where it left off. A turn keeps running when you close the stream, so nothing is lost.
Step 3: Approve spending
In the ask approval mode the agent stops before it spends anything. The chat’s status is waiting_approval and the approval event shows the cost. Answer with approve, always or reject:
curl -s --max-time 150 -X POST "$API/chats/$CHAT/approval" \
-H "Authorization: Bearer $USEAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "decision": "approve" }'
The answer starts a new turn, the same as a message from you:
{
"chatId": "c-u12345-email:[email protected]:74262ac8-...-bot:google-flow",
"email": "[email protected]",
"turn": 2,
"status": "running"
}
On the open stream, turn 2 queued the video, made both images and ended within about 30 seconds. The agent’s choices (Option 1, Option 2) are the tiles the Flow web app shows for each result:
id: 2:5feb253e-0848-422a-bcb8-936995b3c0df
event: message
data: {"id":"2:5feb253e-...","turn":2,"items":[{"kind":"tool_result","tool":"generate_video_from_text","result":{"status":"queued","media_id":"5732c66d-a971-4cd0-92d2-508ef0aecc07","display_name":"Steam rising from coffee mug","model_key":"abra_t2v_6s","aspect_ratio":"VIDEO_ASPECT_RATIO_LANDSCAPE"}}]}
id: 2:m:5732c66d-a971-4cd0-92d2-508ef0aecc07
event: media
data: {"turn":2,"referenceId":"user:12345-email:6a6f...-video:5732c66d-a971-4cd0-92d2-508ef0aecc07","mediaId":"5732c66d-a971-4cd0-92d2-508ef0aecc07","type":"video","status":"queued","tool":"generate_video_from_text","model":"abra_t2v_6s","displayName":"Steam rising from coffee mug"}
id: 2:m:377ecb33-ab2c-4bce-92d4-7bfcf53a9af2
event: media
data: {"turn":2,"referenceId":"user:12345-email:6a6f...-image:377ecb33-ab2c-4bce-92d4-7bfcf53a9af2","mediaId":"377ecb33-ab2c-4bce-92d4-7bfcf53a9af2","type":"image","status":"completed","tool":"generate_image","displayName":"Ceramic coffee mug on table","url":"https://flow-content.google/image/377ecb33-...?Expires=...&Signature=..."}
id: 2:4e37b051-c49c-4331-bc0c-06ccc539b950
event: message
data: {"id":"2:4e37b051-...","turn":2,"items":[{"kind":"text","text":"I've generated your two product shots. Your 6-second video has been scheduled and is waiting in the queue due to high demand. Feel free to check back in a few minutes."}]}
id: 2:end
event: turn
data: {"turn":2,"status":"completed"}
always approves and also switches the project to the auto mode, so the agent stops asking in every chat of the project. reject declines, shown in step 5. Sending a new message instead of answering also moves on without generating.
Step 4: Get the media
Images are ready when the turn ends. Videos usually finish a minute or two later, and the stream sends a second media event for the video when it does. Ours came less than a minute after the turn ended:
id: 2:m:5732c66d-a971-4cd0-92d2-508ef0aecc07
event: media
data: {"turn":2,"referenceId":"user:12345-email:6a6f...-video:5732c66d-a971-4cd0-92d2-508ef0aecc07","mediaId":"5732c66d-a971-4cd0-92d2-508ef0aecc07","type":"video","status":"completed","tool":"generate_video_from_text","model":"abra_t2v_6s","displayName":"Steam rising from coffee mug","url":"https://flow-content.google/video/5732c66d-...?Expires=...&Signature=...","durationSec":6}
Download each url while it is fresh:
curl -s -L -o mug-steam.mp4 "https://flow-content.google/video/5732c66d-...?Expires=...&Signature=..."
The links point to Google and expire about 6 hours after they are issued. Read the chat again with GET /agent/chats/chatId for fresh ones, or get one item’s link from GET /assets/mediaGenerationId with its referenceId. The referenceId also works as referenceImage_N or referenceVideo_1 in POST /images, POST /videos and your next agent message. The two images and the 1280×720 video are in Examples.
The run spent 10 credits: the two Nano Banana 2.1 images cost 0 on this account’s plan, and the 6-second Omni 1.1 Flash video cost 10.
Step 5: Continue or revise
Send the next message to the same chat. Google keeps the conversation, so the agent knows which video “the video” is:
curl -s --max-time 150 -X POST "$API/chats/$CHAT/messages" \
-H "Authorization: Bearer $USEAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "message": "Nice. Revise the video: make it 8 seconds, with a slow camera push-in toward the mug while the steam rises." }'
It answered 202 with "turn": 3, and the agent asked again, this time for 12 credits:
id: 3:approval
event: approval
data: {"turn":3,"cost":12,"images":0,"videos":1,"total":1}
id: 3:end
event: turn
data: {"turn":3,"status":"waiting_approval"}
We had our video, so we declined:
curl -s --max-time 150 -X POST "$API/chats/$CHAT/approval" \
-H "Authorization: Bearer $USEAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "decision": "reject" }'
Turn 4 ended with a reply and no generation, and no credits were spent:
id: 4:91e06fb7-ba5b-4e32-b2a6-ce210be491ba
event: message
data: {"id":"4:91e06fb7-...","turn":4,"items":[{"kind":"text","text":"I've cancelled that generation. Are there any changes you'd like to make to the prompt or concept before we try again?"}]}
id: 4:end
event: turn
data: {"turn":4,"status":"completed"}
Sending a message while a turn runs answers 409, so wait for the turn event first. To pick one of the agent’s choices, send the choice’s text as the next message, as the Flow web app does.
Step 6: Read, list, rename and delete chats
Read the whole chat from Google at any time. Each turn carries your message, everything the agent did and the media with fresh links:
curl -s "$API/chats/$CHAT" -H "Authorization: Bearer $USEAPI_TOKEN"
Our chat after the four turns (agent items and media cut down, ui left out):
{
"chatId": "c-u12345-email:[email protected]:74262ac8-...-bot:google-flow",
"email": "[email protected]",
"projectId": "74262ac8-a54f-4ad7-80d7-3d44a4d6c6c1",
"title": "Sage Green Ceramic Mug Concept",
"created": "2026-10-18T00:47:54.000Z",
"updated": "2026-10-18T00:50:47.236Z",
"status": "idle",
"pendingApproval": null,
"lastError": null,
"turns": [
{
"turn": 1,
"message": { "text": "Create two product shots of a matte sage-green ceramic coffee mug ...", "references": [] },
"agent": [
{ "kind": "text", "text": "I'm going to generate two images of a matte sage-green ceramic coffee mug, then I'll create a 6-second video ..." },
{ "kind": "tool_call", "tool": "ask_for_permission", "args": { "num_total": 3, "total_cost": 10, "num_videos": 1, "num_images": 2 } }
],
"media": []
},
{
"turn": 2,
"message": { "text": "Approve", "references": [] },
"agent": [
{ "kind": "tool_call", "tool": "generate_video_from_text", "args": { "duration": 6, "aspect_ratio": "16:9", "model_display_name": "Omni 1.1 Flash", "prompt": "Cinematic product video of a matte sage-green ceramic coffee mug ..." } },
{ "kind": "tool_call", "tool": "generate_image", "args": { "aspect_ratio": "1:1", "prompt": "A professional product shot of a matte sage-green ceramic coffee mug ..." } },
{ "kind": "text", "text": "I've generated your two product shots. Your 6-second video has been scheduled ..." }
],
"media": [
{ "referenceId": "user:12345-email:6a6f...-video:5732c66d-...", "type": "video", "status": "completed", "model": "abra_t2v_6s", "displayName": "Steam rising from coffee mug", "url": "https://flow-content.google/video/5732c66d-...", "durationSec": 6 },
{ "referenceId": "user:12345-email:6a6f...-image:377ecb33-...", "type": "image", "status": "completed", "displayName": "Ceramic coffee mug on table", "url": "https://flow-content.google/image/377ecb33-..." },
{ "referenceId": "user:12345-email:6a6f...-image:a2d3cd0c-...", "type": "image", "status": "completed", "displayName": "Ceramic coffee mug on wooden sur…", "url": "https://flow-content.google/image/a2d3cd0c-..." }
]
},
{ "turn": 3, "message": { "text": "Nice. Revise the video: make it 8 seconds, ...", "references": [] }, "agent": ["..."], "media": [] },
{
"turn": 4,
"message": { "text": "Reject", "references": [] },
"agent": [{ "kind": "text", "text": "I've cancelled that generation. Are there any changes you'd like to make to the prompt or concept before we try again?" }],
"media": []
}
]
}
Google titled the chat on its own. The status is idle once nothing runs, and pendingApproval holds the cost while the agent waits. Pass ?media=false for a faster read without the link refresh.
List an account’s chats, newest activity first. The list comes from Google, so it also holds chats started in the Flow web app in the same project, and it is how you find a lost chatId:
curl -s "$API/chats?email=$EMAIL" -H "Authorization: Bearer $USEAPI_TOKEN"
Rename a chat. The new title shows in the Flow web app too:
curl -s -X PATCH "$API/chats/$CHAT" \
-H "Authorization: Bearer $USEAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "title": "Sage mug product shoot" }'
{
"chatId": "c-u12345-email:[email protected]:74262ac8-...-bot:google-flow",
"title": "Sage mug product shoot"
}
The list then read:
{
"chats": [
{
"chatId": "c-u12345-email:[email protected]:74262ac8-...-chat:55023a0f-...-bot:google-flow",
"email": "[email protected]",
"title": "Sage mug product shoot",
"created": "2026-10-18T00:47:54.000Z",
"updated": "2026-10-18T00:51:01.773Z",
"running": false
},
{
"chatId": "c-u12345-email:[email protected]:74262ac8-...-chat:1d9d42ed-...-bot:google-flow",
"email": "[email protected]",
"title": "Stylish Female Character Action Sequence",
"created": "2026-10-17T03:50:34.000Z",
"updated": "2026-10-17T04:57:26.375Z",
"running": false
}
]
}
Delete a chat’s conversation. It answers 204 with no body, the chat leaves the list and the Flow web app, and the images and videos it made stay in the project:
curl -s -X DELETE "$API/chats/$CHAT" -H "Authorization: Bearer $USEAPI_TOKEN"
Rename and delete answer 409 while a turn runs.
Step 7: Settings for approval, instructions and default models
Each Flow project has agent settings, the same as the agent panel in the Flow web app. GET /agent/settings reads them, and also lists the image and video models the account’s Google plan has, with what one generation costs on that plan:
curl -s "$API/settings?email=$EMAIL" -H "Authorization: Bearer $USEAPI_TOKEN"
PUT /agent/settings changes only the fields you send. Before the run we set the ask mode and the defaults:
curl -s -X PUT "$API/settings" \
-H "Authorization: Bearer $USEAPI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "'"$EMAIL"'",
"approval": "ask",
"imageDefaults": { "model": "nano-banana-2", "aspectRatio": "1:1", "count": 2 },
"videoDefaults": { "model": "omni-flash", "aspectRatio": "16:9", "count": 1 }
}'
The response is the project’s settings after the change:
{
"projectId": "74262ac8-a54f-4ad7-80d7-3d44a4d6c6c1",
"approval": "ask",
"instructions": [],
"imageDefaults": { "model": "nano-banana-2", "aspectRatio": "1:1", "count": 2 },
"videoDefaults": { "model": "omni-flash", "aspectRatio": "16:9", "count": 1 },
"models": {
"image": [
{ "model": "nano-banana-pro", "label": "Nano Banana Pro", "credits": 0 },
{ "model": "nano-banana-2-lite", "label": "Nano Banana 2 Lite", "credits": 0 },
{ "model": "nano-banana-2", "label": "Nano Banana 2.1", "credits": 0 }
],
"video": [
{ "model": "omni-flash", "label": "Omni 1.1 Flash", "credits": 4 },
{ "model": "veo-3.1-lite", "label": "Veo 3.1 - Lite", "credits": 5 },
{ "model": "veo-3.1-fast", "label": "Veo 3.1 - Fast", "credits": 10 },
{ "model": "veo-3.1-quality", "label": "Veo 3.1 - Quality", "credits": 100 }
]
}
}
credits is the lowest cost of one generation on the account’s plan, so a longer or higher-resolution clip costs more: our 6-second 720p Omni 1.1 Flash clip cost 10, and the 8-second revision would have cost 12. Your plan may show different numbers. The settings fields:
approval:ask(the agent stops and shows the cost) orauto(it spends without asking).instructions: up to 3 standing instructions the agent follows in every chat of the project, each with optional reference images. The array replaces the project’s instructions, so send the full list.imageDefaultsandvideoDefaults: the model, aspect ratio and count the agent uses when a message does not say otherwise, picked frommodels.
The settings apply to every chat in the project, including chats in the Flow web app.
The same from an AI assistant
The hosted Google Flow MCP server gives Claude Code, Cursor, VS Code and other MCP clients the same chat as 12 tools, with nothing to install. In Claude Code:
claude mcp add --transport http useapi-google-flow https://api.useapi.net/v1/google-flow/mcp --header "Authorization: Bearer YOUR_API_TOKEN"
Then ask the assistant for the mug shots in plain words. The assistant sends the brief with flow_agent_chat and answers the agent’s request to spend credits with flow_agent_answer_approval. The server is listed in the official MCP Registry as net.useapi/google-flow.
To see a full chat client first, open the live demo. Paste your API token, pick an account and chat with the agent, with its images and videos inline, attachments, approval cards and settings. Its source is five small files with no build step on GitHub in google-flow-api/demo, and the Agent Live Demo page maps each feature to its API call.
Examples
What this run made, with no edits. The prompts are the agent’s own, written from our brief.
Nano Banana 2.1 » two product shots of the sage-green mug (turn 2)
Both 1024×1024 JPEG, 0 credits on this account’s plan.
Omni 1.1 Flash » 6 seconds of steam rising from the mug (turn 2)
1280×720 with sound, 6 seconds, 10 credits.
Frequently asked questions
- How much does chatting with Google Flow Agent cost? Chatting costs no Flow credits. The images and videos the agent makes cost the usual Flow price, the same as POST /images and POST /videos, and in the
askmode you see each cost before anything is spent. API access is a flat useapi.net subscription for every service, with no per-call charge. - Which Google plans does the Agent API work on? Every Google AI plan, including a free account. What the agent can generate follows the account’s plan, and GET /agent/settings lists the models the plan has with their credits.
- What is the difference between
ask,autoandalways?askandautoare the project’s approval modes: inask(the default) the agent stops and shows the cost, inautoit spends without asking.alwaysis an answer to one approval: it approves and switches the project toauto. Switch back with PUT /agent/settingsapproval: "ask". - Do chats started in the Flow web app show up in the API? Yes, when they are in the same project. GET /agent/chats lists them, and every endpoint above works on them. Chats started through the API show up in the web app too. Pass
projectIdfrom GET /assets/projects/emailto work in a project other than the agent’s own. - Can I get a webhook instead of holding a stream open? Yes. Pass
replyUrlto POST /agent/chats, the messages call or the approval call, and the API sends onePOSTwith the turn’s messages, media, status and any pending approval when the turn ends. Videos usually arrive there stillqueued, so get their links from GET or the stream’smediaevents. - What are the limits? One turn at a time per chat, at most 3 running turns per account, and up to 10 minutes per turn, Google’s own limit. Google also caps agent messages per account per day without publishing the number, and the API answers
429for that account until midnight UTC. See Limits.
Conclusion
Visit our Discord Server or
Telegram Channel for any support questions and concerns.
Check our GitHub repo with code examples, including the agent chat client in demo/.
An AI assistant can chat with Google Flow Agent for you too: the hosted Google Flow MCP server works with Claude Code, Cursor and VS Code, on your own Google account.

