> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usenotra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK (Community)

> Use the community-maintained notra crate for a type-safe Rust integration.

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](https://crates.io/crates/notra) and
  [GitHub](https://github.com/lassejlv/notra-sdk-rust).
</Info>

## Installation

Add the crate to your `Cargo.toml`:

```toml theme={"theme":{"light":"github-light","dark":"github-dark"}}
[dependencies]
notra = "0.1.1"
tokio = { version = "1", features = ["full"] }
```

## Quick Start

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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:

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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:

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let result = notra.content()
    .get_post("post_abc")
    .send()
    .await?;

if let Some(post) = result.post {
    println!("{}: {}", post.title, post.status);
}
```

### Update a post

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
notra.content()
    .delete_post("post_abc")
    .send()
    .await?;
```

## Post Generation

### Queue a generation job

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
let job = notra.content()
    .create_brand_identity("https://example.com")
    .name("My Brand")
    .send()
    .await?;

// Poll for completion
let 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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
notra.content()
    .delete_brand_identity("identity-id")
    .send()
    .await?;
```

## Integrations

### List integrations

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
notra.content()
    .delete_integration("integration-id")
    .send()
    .await?;
```

## Error Handling

The SDK uses a `NotraError` enum with three variants:

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
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`:

```rust theme={"theme":{"light":"github-light","dark":"github-dark"}}
use notra::models::{
    Post, PostStatus, ContentType, Sort,
    Pagination, Organization,
    LookbackWindow, DataPoints, Repository,
    BrandIdentity, ToneProfile, Language,
    GithubIntegration, LinearIntegration,
    PostGenerationJob, JobStatus,
};
```
