context69-sdk 0.5.0

Async Rust SDK for the Context69 HTTP API.
Documentation

context69-sdk

Async Rust SDK for the Context69 HTTP API.

context69-sdk is a PAT-only client. Initialize it with a personal access token that starts with ctx_pat_, then navigate the resource tree to call an API.

Initialization

use context69_sdk::Context69Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Context69Client::builder()
        .base_url("http://127.0.0.1:8096")?
        .with_personal_access_token("ctx_pat_example_token")?
        .build()?;

    let me = client.me().await?;
    println!("hello {}", me.user.login_name);
    Ok(())
}

The builder rejects empty tokens and non-PAT tokens. Protected APIs return Error::AuthenticationRequired if no PAT is configured.

Groups and nested resources

use context69_sdk::{
    Context69Client,
    contracts::{CreateGroupRequest, GroupKind, Visibility},
};

# async fn example(client: Context69Client) -> Result<(), context69_sdk::Error> {
client.groups().create(&CreateGroupRequest {
    parent_group_path: None,
    group_key: "ops".to_string(),
    name: "Operations".to_string(),
    visibility: Visibility::Private,
    kind: Some(GroupKind::Shared),
}).await?;

let group = client.group("ops").get().await?;
let members = client.group("ops").members().list().await?;
let tree = client.group("ops").library().tree().await?;
# Ok(())
# }

Group handles own the group path, so nested calls do not repeat it:

# use context69_sdk::Context69Client;
# async fn example(client: Context69Client, folder_id: uuid::Uuid, file_id: uuid::Uuid) -> Result<(), context69_sdk::Error> {
let group = client.group("ops/runbooks");
group.source_folder(folder_id).sync().await?;
let file = group.library().file(file_id).get().await?;
# Ok(())
# }

Sources

# use context69_sdk::{Context69Client, contracts::SourceConfigInput};
# async fn example(client: Context69Client, request: SourceConfigInput) -> Result<(), context69_sdk::Error> {
client.sources().create(&request).await?;
client.source(&request.source_key).sync().await?;

let connections = client.source_connections().list().await?;
client.source_connection("warehouse").delete().await?;
# Ok(())
# }

Library

use context69_sdk::{
    Context69Client,
    contracts::{CreateTextRequest, LibraryTextContentFormat},
};

# async fn example(client: Context69Client) -> Result<(), context69_sdk::Error> {
let upload = client.library().texts().create(&CreateTextRequest {
    folder_id: None,
    title: "Runbook".to_string(),
    content: "# Step 1".to_string(),
    content_format: LibraryTextContentFormat::Markdown,
    source_uri: None,
    summary: None,
}).await?;

let tree = client.group("ops/runbooks").library().tree().await?;
# Ok(())
# }

Multipart uploads use reqwest::multipart::Part:

# use context69_sdk::Context69Client;
use reqwest::multipart::Part;

# async fn example(client: Context69Client) -> Result<(), context69_sdk::Error> {
client.library().files().upload(
    None,
    vec![Part::text("# Notes").file_name("notes.md")],
).await?;
# Ok(())
# }

Settings and search

# use context69_sdk::{Context69Client, contracts::SearchRequest};
# async fn example(client: Context69Client, request: SearchRequest) -> Result<(), context69_sdk::Error> {
let runtime = client.settings().runtime().get().await?;
let docling = client.settings().docling().get().await?;
let result = client.search().execute(&request).await?;
let document = client.document(result.hits[0].document_id).get().await?;
# Ok(())
# }

Resource tree

  • client.user_directory(): user search
  • client.groups() / client.group(path): groups, children, members
  • client.sources() / client.source(key): source collection and item operations
  • client.source_connections() / client.source_connection(name): connection operations
  • client.library(): personal library folders, texts, files, and jobs
  • client.group(path).library(): group-scoped library resources
  • client.group(path).source_folders() / source_folder(id): group source folders
  • client.settings(): runtime, provider accounts, Docling, and search settings
  • client.search() / client.document(id): search and document lookup
  • root client: me() and healthz()

PAT scopes

  • workspace: user directory, groups, memberships
  • sources: source connections, sources, group source folders, sync
  • library: personal and group library resources
  • settings: runtime, provider accounts, Docling, search settings
  • search: search and document APIs

Migrating from 0.4

Version 0.5 removes the old grouped service methods. Common replacements:

client.workspace().list_groups()                         -> client.groups().list()
client.workspace().get_group(path)                      -> client.group(path).get()
client.sources().sync_source(key)                       -> client.source(key).sync()
client.sources().sync_group_source_folder(path, id)     -> client.group(path).source_folder(id).sync()
client.library().get_library_tree()                     -> client.library().tree()
client.library().get_group_library_file(path, id)       -> client.group(path).library().file(id).get()
client.settings().get_docling_settings()                -> client.settings().docling().get()
client.search().search(request)                         -> client.search().execute(&request)
client.search().get_document(id)                        -> client.document(id).get()

Login, refresh, logout, PAT management, and admin-user APIs remain intentionally excluded. HTTP 401 Unauthorized responses are returned directly; the SDK does not retry with refresh-cookie logic.