Rust SDK

Install, initialize, and use the ConvoMem Rust SDK.

The ConvoMem Rust SDK is async-first, built on tokio and reqwest.

Install

cargo add convomem

Or add to Cargo.toml:

[dependencies]
convomem = "0.3" # available only after the SDK release is published
tokio = { version = "1", features = ["full"] }

Initialize

use convomem::ConvoMem;
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = ConvoMem::new(std::env::var("CONVOMEM_API_KEY")?);
    Ok(())
}

With a custom timeout:

use convomem::ConvoMem;
use std::time::Duration;
 
let client = ConvoMem::builder()
    .api_key(std::env::var("CONVOMEM_API_KEY")?)
    .timeout(Duration::from_secs(15))
    .build()?;

The default timeout is 30 seconds. The Rust SDK does not retry automatically — handle 429 and 5xx responses yourself (see Errors & limits).

ConvoMem is not Clone. To use one client from several tasks, wrap it in an Arc:

use std::sync::Arc;
 
let client = Arc::new(ConvoMem::new(std::env::var("CONVOMEM_API_KEY")?));
let worker = Arc::clone(&client);
tokio::spawn(async move {
    // worker.capture(…).await
});

Identity

CustomerIdentity routes every call to the right customer. When customer_id is set the SDK uses the direct /customers/:id/… path. Otherwise the server resolves the customer from email, phone, or external_id.

use convomem::CustomerIdentity;
 
// By known ID — most direct
let identity = CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12");
 
// By email — server resolves the customer
let identity = CustomerIdentity::from_email("[email protected]");
 
// By phone (E.164 format recommended)
let identity = CustomerIdentity::from_phone("+14155550100");
 
// By external system ID
let identity = CustomerIdentity::from_external_id("crm-456");

Capture

Send conversation turns for background memory extraction. Acceptance is not completion: poll the returned capture ID before checking extracted memories.

use convomem::{ConvoMem, CustomerIdentity, Message};
 
let result = client.capture(
    vec![
        Message { role: "user".into(),      content: "I need help with my order".into(), ..Default::default() },
        Message { role: "assistant".into(), content: "Of course, let me look that up.".into(), ..Default::default() },
    ],
    &CustomerIdentity::from_email("[email protected]"),
    Some("CHAT"),   // channel
    None,           // user_name
    None,           // idempotency_key
).await?;

Use client.get_capture_status(&result.capture_id).await? on the returned capture response, with bounded polling, a delay, and a timeout. Do not treat a failed or still-processing capture as an empty memory result.

For options beyond channel, user name, and idempotency key — such as closing the conversation on the final turns — build a CaptureRequest and call capture_with:

use convomem::{CaptureRequest, Message};
 
client.capture_with(&CaptureRequest {
    messages: Some(vec![
        Message { role: "assistant".into(), content: "All sorted — anything else?".into(), ..Default::default() },
    ]),
    phone_number: Some("+14155550101".into()),
    channel: Some("VOICE".into()),
    end_conversation: Some(true),
    outcome: Some("Refund issued".into()),
    ..Default::default()
}).await?;

Cold open

At the start of a conversation, before the customer has said why they are calling, get the profile and a call-opening brief that leads with any unresolved issue:

let brief = client.cold_open(
    &CustomerIdentity::from_phone("+14155550100"),
    Some(true), // auto_create
    None,       // user_name
    None,       // max_tokens
).await?;
 
if let Some(context) = &brief.context {
    println!("{}", context);
}

Look up context

Recall semantically relevant memories for a topic before generating a reply.

let ctx = client.lookup(
    "order and shipping preferences",
    &CustomerIdentity::from_email("[email protected]"),
    None,   // auto_create
    None,   // user_name
    None,   // max_tokens — use the configured context budget
).await?;
 
println!("Context: {}", ctx.context);
for mem in &ctx.memories {
    println!("  - {:?}", mem.content);
}

Agent loop pattern

use convomem::{ConvoMem, CustomerIdentity, Message};
 
let client = ConvoMem::new(std::env::var("CONVOMEM_API_KEY")?);
 
async fn handle_message(
    client: &ConvoMem,
    user_msg: &str,
    email: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    let identity = CustomerIdentity::from_email(email);
 
    // 1. Recall context
    let ctx = client.lookup("customer support", &identity, None, None, None).await?;
 
    // 2. Call your LLM (pseudo-code)
    let reply = call_llm(&ctx.context, user_msg).await;
 
    // 3. Capture the turns
    client.capture(
        vec![
            Message { role: "user".into(),      content: user_msg.into(), ..Default::default() },
            Message { role: "assistant".into(), content: reply.clone(), ..Default::default() },
        ],
        &identity,
        Some("CHAT"),
        None,
        None,
    ).await?;
 
    Ok(reply)
}

Memories

use convomem::CustomerIdentity;
 
let identity = CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12");
 
// List all memories
let result = client.list_memories(&identity, None, None, None).await?;
for mem in &result.memories {
    println!("- {:?}", mem.content);
}
 
// Add manually
let memory = client.add_memory(
    "Prefers email contact",
    &identity,
    Some("preference"),
).await?;
 
// Update (requires customer_id)
let updated = client.update_memory(
    "c2a7e4b1-9f3d-4a6e-8b5c-1d2e3f4a5b6c",
    &identity,
    Some("Prefers email and text contact"),
    None,
).await?;
 
// Delete (requires customer_id)
client.delete_memory("c2a7e4b1-9f3d-4a6e-8b5c-1d2e3f4a5b6c", &identity).await?;

Customers

use convomem::{CustomerCreateRequest, CustomerIdentity, CustomerUpdateRequest};
 
// Create
let customer = client.create_customer(&CustomerCreateRequest {
    name: Some("Alice".into()),
    email: Some("[email protected]".into()),
    ..Default::default()
}).await?;
 
// Get — by ID (direct) or by identity (server resolves)
let customer = client.get_customer(
    &CustomerIdentity::from_email("[email protected]")
).await?;
println!("Customer ID: {}", customer.id);
 
// Update
let updated = client.update_customer(
    &CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12"),
    &CustomerUpdateRequest {
        name: Some("Alice Smith".into()),
        ..Default::default()
    },
).await?;
 
// List
let result = client.list_customers(Some(1), Some(20), None).await?;
println!("{} of {}", result.customers.len(), result.total);
 
// Delete
client.delete_customer(&CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12")).await?;

Conversations

use convomem::CustomerIdentity;
 
let identity = CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12");
 
// Start
let conversation = client.start_conversation(&identity, "CHAT").await?;
println!("Conversation: {}", conversation.id);
 
// List
let result = client.list_conversations(&identity, None, None, None).await?;
 
// End — path-based when both IDs are known
client.end_conversation(
    Some("87c53d10-85f8-4447-920f-b1e847c23a1a"),
    &identity,
    Some("Resolved billing issue"),
).await?;
 
// End — flat route: closes the customer's active conversation
client.end_conversation(
    None,
    &CustomerIdentity::from_email("[email protected]"),
    Some("Resolved"),
).await?;
 
// Escalate
client.escalate_conversation(
    Some("87c53d10-85f8-4447-920f-b1e847c23a1a"),
    &identity,
    Some("Complex billing dispute"),
).await?;

Handoff

Generate a briefing for the human agent taking over the conversation.

use convomem::CustomerIdentity;
 
let handoff = client.get_handoff(
    &CustomerIdentity::from_email("[email protected]"),
    None,   // fresh
    None,   // narrative
).await?;
 
if handoff.found {
    if let Some(narrative) = &handoff.narrative {
        println!("Narrative: {}", narrative);
    }
    for mem in &handoff.key_memories {
        println!("  - {}", mem.content);
    }
}

Route a live transfer

Resolve which human destination a transfer should go to. Destinations are configured in the dashboard.

let route = client.route_handoff(
    &CustomerIdentity::from_phone("+14155550100"),
    None, // format
).await?;
 
if let Some(destination) = &route.destination {
    println!("{} ({})", destination.value, route.source);
}

Embed tokens

use convomem::CustomerIdentity;
 
let result = client.create_embed_token(
    &CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12"),
    Some(3600),  // ttl_seconds
).await?;
 
println!("Token: {}", result.token);
println!("Expires in: {}s", result.expires_in);

Error handling

use convomem::ConvoMemError;
 
match client.get_customer(&CustomerIdentity::from_id("f3b39d1b-2522-4a00-98df-8cdde783ba12")).await {
    Ok(customer) => println!("Found: {}", customer.id),
    Err(ConvoMemError::Api { status, message }) => {
        println!("API error {}: {}", status, message);
    }
    Err(e) => println!("Other error: {}", e),
}

Other SDKs