Rust SDK (Community)
Use the community-maintained notra crate for a type-safe Rust integration.
reference · intermediate · 12m
applies to notra-api, rust
The notra crate is a community-maintained Rust SDK for the Notra API. It provides typed request builders, response structs, and async support via tokio.
Info
This SDK is community-maintained. Source and docs are available at crates.io/crates/notra and GitHub.
Installation
Add the crate to your Cargo.toml:
[dependencies]notra = "0.1.1"tokio = { version = "1", features = ["full"] }Quick Start
use notra::Notra;#[tokio::main]async fn main() -> Result<(), notra::NotraError> { let notra = Notra::builder() .bearer_auth(std::env::var("NOTRA_API_KEY").unwrap()) .build()?; let result = notra.content() .list_posts() .send() .await?; for post in &result.posts { println!("{} ({})", post.title, post.id); } Ok(())}Authentication
Pass your API key via the builder:
use notra::Notra;let notra = Notra::builder() .bearer_auth(std::env::var("NOTRA_BEARER_AUTH").unwrap()) .build()?;You can also configure a custom base URL and timeout:
use std::time::Duration;use notra::Notra;let notra = Notra::builder() .bearer_auth("your-api-key") .server_url("https://api.usenotra.com") // default .timeout(Duration::from_secs(60)) // default: 30s .build()?;Posts
List posts
use notra::Notra;use notra::models::{PostStatus, Sort};let result = notra.content() .list_posts() .status(PostStatus::Published) .sort(Sort::Desc) .limit(10) .page(1) .send() .await?;for post in &result.posts { println!("{}", post.title);}println!( "Page {} of {}", result.pagination.current_page, result.pagination.total_pages.unwrap_or(1));Get a single post
let result = notra.content() .get_post("post_abc") .send() .await?;if let Some(post) = result.post { println!("{}: {}", post.title, post.status);}Update a post
use notra::models::PostStatus;let updated = notra.content() .update_post("post_abc") .title("Ship notes for week 11") .markdown("# Ship notes\n\nWe shipped a faster editor.") .status(PostStatus::Published) .send() .await?;println!("{:?}", updated.post);Delete a post
notra.content() .delete_post("post_abc") .send() .await?;Post Generation
Queue a generation job
use notra::models::*;let job = notra.content() .create_post_generation(ContentType::Changelog) .lookback_window(LookbackWindow::Last7Days) .brand_identity_id("brand-id") .github_integration_ids(vec!["integration-1".into()]) .repositories(vec![ Repository { owner: "my-org".into(), repo: "my-repo".into() }, ]) .data_points(DataPoints { commits: true, pull_requests: true, releases: true, linear_data: false, }) .send() .await?;println!("Job started: {}", job.job.id);Poll job status
let status = notra.content() .get_post_generation("job-id") .send() .await?;println!("Status: {:?}", status.job.status);for event in &status.events { println!(" {} at {}", event.event_type, event.created_at);}Brand Identities
List brand identities
let result = notra.content() .list_brand_identities() .send() .await?;for bi in &result.brand_identities { println!("{} (default: {})", bi.name, bi.is_default);}Generate a brand identity from a website
let job = notra.content() .create_brand_identity("https://example.com") .name("My Brand") .send() .await?;// Poll for completionlet status = notra.content() .get_brand_identity_generation(&job.job.id) .send() .await?;println!("Step: {:?}, Status: {:?}", status.job.current_step, status.job.status);Update a brand identity
use notra::models::{ToneProfile, Language};let updated = notra.content() .update_brand_identity("identity-id") .name("Updated Brand") .tone_profile(ToneProfile::Professional) .language(Language::English) .audience("Developers") .is_default(true) .send() .await?;Delete a brand identity
notra.content() .delete_brand_identity("identity-id") .send() .await?;Integrations
List integrations
let integrations = notra.content() .list_integrations() .send() .await?;for gh in &integrations.github { println!("{}/{} ({})", gh.owner, gh.repo, gh.default_branch);}Connect a GitHub repository
let gh = notra.content() .create_github_integration("my-org", "my-repo") .branch("main") .auth_token("ghp_xxx") // optional, for private repos .send() .await?;println!("Connected: {}", gh.github.display_name);Disconnect an integration
notra.content() .delete_integration("integration-id") .send() .await?;Error Handling
The SDK uses a NotraError enum with three variants:
use notra::{Notra, NotraError};match notra.content().get_post("bad-id").send().await { Ok(response) => { if let Some(post) = response.post { println!("Found: {}", post.title); } } Err(NotraError::Api { status, message }) => { eprintln!("API error ({}): {}", status, message); } Err(NotraError::Http(e)) => eprintln!("Network error: {e}"), Err(NotraError::Builder(msg)) => eprintln!("Config error: {msg}"),}Types
All types are exported under notra::models:
use notra::models::{ Post, PostStatus, ContentType, Sort, Pagination, Organization, LookbackWindow, DataPoints, Repository, BrandIdentity, ToneProfile, Language, GithubIntegration, LinearIntegration, PostGenerationJob, JobStatus,};Did this work on your setup?
Not rated yet