Rust SDK
Install, initialize, and use the ConvoMem Rust SDK.
The ConvoMem Rust SDK is async-first, built on tokio and reqwest.
These examples target SDK 0.3.0, released with ConvoMem v1.2.0.
Install
cargo add convomemOr 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
});Never embed an API key in client-side code. All ConvoMem calls should originate from your backend. For browser widgets, use embed tokens.
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),
}