Gemini Interactions API
The Gemini Interactions API is Google's stateful conversation endpoint. Unlike the
standard
generateContent/streamGenerateContent endpoints, Interactions
uses previous_interaction_id for server-side conversation state, flat
outputs[] instead of nested candidates[].content.parts[], and
typed SSE events with an event_type field inside each JSON payload.
Endpoint
| Method | Path | Format |
|---|---|---|
| POST | /v1beta/interactions | SSE (typed JSON events) |
Quick Start
Set up a minimal mock that responds to a Gemini Interactions request:
import { LLMock } from "@copilotkit/aimock";
const mock = new LLMock();
mock.on({ userMessage: "hello" }, { content: "Hi there!" });
await mock.listen(4010);
// Client sends:
// POST /v1beta/interactions
// { model: "gemini-2.5-flash", input: "hello", stream: true }
Request Format
The Interactions API accepts several input shapes. aimock normalizes all of them into the unified fixture-matching format.
String Input (Simple Prompt)
{
"model": "gemini-2.5-flash",
"input": "What is the capital of France?",
"stream": true
}
Turn[] Input (Multi-Turn)
{
"model": "gemini-2.5-flash",
"input": [
{ "role": "user", "parts": [{ "type": "text", "text": "Hello" }] },
{ "role": "model", "parts": [{ "type": "text", "text": "Hi there!" }] },
{ "role": "user", "parts": [{ "type": "text", "text": "Tell me a joke" }] }
],
"stream": true
}
Content[] with Function Result (Tool Response)
{
"model": "gemini-2.5-flash",
"input": [
{
"role": "user",
"parts": [
{ "type": "function_result", "name": "get_weather", "call_id": "call_abc123", "result": "72F sunny" }
]
}
],
"previous_interaction_id": "interaction_abc123",
"stream": true
}
Stateful Chaining
The previous_interaction_id field links turns together on the server side.
Each response includes an interaction_id that the client passes as
previous_interaction_id in the next request, eliminating the need to resend
full conversation history.
Fixture Matching
Fixtures use the same match object as all other providers. The most common
matchers for Interactions are userMessage and sequenceIndex.
userMessage Matching
const fixture = {
match: { userMessage: "hello" },
response: { content: "Hi from Gemini Interactions!" },
};
sequenceIndex for Multi-Turn Chains
Since the Interactions API is stateful, multi-turn conversations are the primary use case.
Use sequenceIndex to match by turn position:
const fixtures = [
{
match: { sequenceIndex: 0 },
response: {
toolCalls: [{ name: "get_weather", arguments: { city: "NYC" } }]
},
},
{
match: { sequenceIndex: 1 },
response: { content: "The weather in NYC is 72F and sunny." },
},
];
const instance = await createServer(fixtures);
// Turn 1: model calls get_weather tool
// Turn 2: after tool result, model produces final text
SSE Event Format
Unlike standard Gemini SSE (bare data: chunks), the Interactions API uses
typed events. Each SSE line is a data: JSON object containing an
event_type field.
Event Types
| Event Type | Description |
|---|---|
interaction.start |
Opening event with interaction_id and model metadata |
content.start |
Marks beginning of an output part |
content.delta |
Incremental text or function_call chunk |
content.stop |
Marks completion of an output part |
interaction.complete |
Final event with usage metadata and finish reason |
Text Streaming Example
data: {"event_type":"interaction.start","interaction":{"id":"int_abc123","status":"in_progress"},"event_id":"evt_1"}
data: {"event_type":"content.start","index":0,"content":{"type":"text"},"event_id":"evt_2"}
data: {"event_type":"content.delta","index":0,"delta":{"type":"text","text":"Hi "},"event_id":"evt_3"}
data: {"event_type":"content.delta","index":0,"delta":{"type":"text","text":"there!"},"event_id":"evt_4"}
data: {"event_type":"content.stop","index":0,"event_id":"evt_5"}
data: {"event_type":"interaction.complete","interaction":{"id":"int_abc123","status":"completed","usage":{"total_input_tokens":5,"total_output_tokens":3,"total_tokens":8}},"event_id":"evt_6"}
Tool Call Streaming Example
data: {"event_type":"interaction.start","interaction":{"id":"int_def456","status":"in_progress"},"event_id":"evt_1"}
data: {"event_type":"content.start","index":0,"content":{"type":"function_call"},"event_id":"evt_2"}
data: {"event_type":"content.delta","index":0,"delta":{"type":"function_call","id":"tc_abc123","name":"get_weather","arguments":{"city":"NYC"}},"event_id":"evt_3"}
data: {"event_type":"content.stop","index":0,"event_id":"evt_4"}
data: {"event_type":"interaction.complete","interaction":{"id":"int_def456","status":"requires_action","usage":{"total_input_tokens":8,"total_output_tokens":12,"total_tokens":20}},"event_id":"evt_5"}
Recording
To record real Gemini Interactions API traffic, use the gemini provider
settings (same base URL, same API key). The Interactions endpoint shares the Gemini
infrastructure:
import { LLMock } from "@copilotkit/aimock";
const mock = new LLMock({
record: {
providers: {
gemini: "https://generativelanguage.googleapis.com",
},
},
});
await mock.listen(4010);
Unmatched requests to /v1beta/interactions are proxied to the real API, and
the response is recorded as a new fixture.
Integration with TanStack AI
TanStack AI provides a geminiTextInteractions() adapter for the Interactions
API. When your client uses this adapter, point it at your aimock instance and the same
fixtures will serve the responses. See the
TanStack AI docs for adapter
configuration.
Known Limitations
The Gemini Interactions API is currently in beta. Both the real API and aimock's support for it are subject to change as the API stabilizes.
- Beta API — event types and request shapes may shift between releases.
- Text output + function tools only — the current scope covers text generation and function calling.
-
Built-in tools not supported — Google's built-in tools
(
google_search,code_execution) are not yet mocked. - Non-text modalities not supported — image, audio, and video inputs/outputs are not handled by this endpoint.