Skip to main content

Overview

The Client struct is the main entry point for all interactions with the t3.chat API. It manages authentication, session handling, conversation state, and API communication.

Structure

The Client maintains several key pieces of state:
  • Authentication: Cookies and Convex session ID for API authentication
  • Thread Management: Optional thread ID for conversation continuity
  • Message History: Vector of messages in the current conversation
  • HTTP Client: Configured reqwest client with proper headers

Lifecycle

Creating a Client

Initialize a new client with your authentication credentials:
use t3router::t3::client::Client;

let cookies = std::env::var("COOKIES").expect("COOKIES not set");
let convex_session_id = std::env::var("CONVEX_SESSION_ID").expect("CONVEX_SESSION_ID not set");

let mut client = Client::new(cookies, convex_session_id);
Method Signature:
pub fn new(cookies: String, convex_session_id: String) -> Self

Initializing the Session

Before sending messages, initialize the client to establish a session:
if client.init().await? {
    println!("Client initialized successfully");
}
Method Signature:
pub async fn init(&self) -> Result<bool, reqwest::Error>
This method sends a GET request to the t3.chat homepage to establish the session.

Sending Messages

Send messages to the API and receive responses:
use t3router::t3::message::{Message, Type};
use t3router::t3::config::Config;

let config = Config::new();
let response = client.send(
    "gemini-2.5-flash-lite",
    Some(Message::new(Type::User, "Hello!".to_string())),
    Some(config),
).await?;

println!("Assistant: {}", response.content);
Method Signature:
pub async fn send(
    &mut self,
    model: &str,
    new_message: Option<Message>,
    config: Option<Config>,
) -> Result<Message, reqwest::Error>

Session Management

Refreshing Sessions

The client automatically refreshes sessions when sending messages, but you can manually refresh:
client.refresh_session().await?;
Method Signature:
pub async fn refresh_session(&mut self) -> Result<bool, reqwest::Error>
This updates the wos-session cookie by calling the active sessions endpoint.

Thread Management

Threads represent conversation contexts in t3.chat:
// Get current thread ID
if let Some(thread_id) = client.get_thread_id() {
    println!("Thread ID: {}", thread_id);
}
Method Signature:
pub fn get_thread_id(&self) -> Option<&String>
Threads are automatically created on the first message and maintained across subsequent messages in the same conversation.

Image Operations

Generating and Downloading Images

For image generation models, use send_with_image_download to automatically download generated images:
use std::path::Path;

let save_path = Path::new("output/image.png");
let response = client.send_with_image_download(
    "gpt-image-1",
    Some(Message::new(Type::User, "Create a robot".to_string())),
    Some(config),
    Some(save_path),
).await?;
Method Signature:
pub async fn send_with_image_download(
    &mut self,
    model: &str,
    new_message: Option<Message>,
    config: Option<Config>,
    save_path: Option<&Path>,
) -> Result<Message, Box<dyn std::error::Error>>

Manual Image Downloads

Download images from URLs:
let base64_data = client.download_image(
    "https://example.com/image.png",
    Some(Path::new("output/downloaded.png")),
).await?;
Method Signature:
pub async fn download_image(
    &self,
    url: &str,
    save_path: Option<&Path>,
) -> Result<String, Box<dyn std::error::Error>>

Response Parsing

The client automatically parses EventStream responses from the API: Method Signature:
pub async fn parse_response(
    &self,
    response: &str,
) -> Result<(String, Option<String>, Option<String>), String>
Returns:
  • Text content (String)
  • Optional image URL (Option<String>)
  • Optional base64 image data (Option<String>)
This method handles various response types including text deltas, image generation events, and tool outputs.

Error Handling

Most Client methods return Result types:
match client.send(model, message, config).await {
    Ok(response) => println!("Success: {}", response.content),
    Err(e) => eprintln!("Error: {}", e),
}
The send method returns an error message as a valid Message object when no messages are queued, rather than returning an Err result.

Best Practices

Call client.init().await? after creating a new client to establish a proper session.
Create one client per authentication context and reuse it for multiple conversations to maintain session state.
Use new_conversation() when starting fresh topics to avoid context bleed between unrelated conversations.
Always check response.content_type when using image generation models to handle both text and image responses.

Build docs developers (and LLMs) love