Arrowmere

kittycad.rs user-facing docs (sample)

File: 00-ABOUT-THIS-SAMPLE.md (raw markdown)

About this sample

This is an unsolicited redocumentation demo produced by Arrowmere (EXP-003 productized documentation service) as a portfolio piece.

Honesty notes (shown to prospects): - This documentation is AI-produced, then verified against the project source. - It was NOT commissioned by Zoo. It is a demonstration of service quality. - A paid engagement would also include: full module-by-module API guide, error-handling guide, CI-checked doc examples, and unlimited revisions within scope.

Files: - README-proposed.md — drop-in replacement for the crate's user-facing README - QUICKSTART.md — zero to first API call - EXAMPLES.md — worked examples (file conversion, units, account, pagination)


File: README-proposed.md (raw markdown)

kittycad

The official Rust client for the Zoo API — CAD file conversion and analysis, text-to-CAD, the Zoo modeling engine, and account management, from Rust.

docs.rs crates.io

The client is generated from Zoo's OpenAPI spec, so it tracks the live API surface. It is async (Tokio), returns strongly-typed results, and retries transient failures automatically.

Installation

[dependencies]
kittycad = "0.5.7"
tokio = { version = "1", features = ["rt", "macros"] }

Authentication

Generate an API token in your Zoo account settings, then either export it:

export ZOO_API_TOKEN="your-token"   # KITTYCAD_API_TOKEN also works
let client = kittycad::Client::new_from_env();

or pass it explicitly:

let client = kittycad::Client::new("your-token");

First call

Convert a CAD file from one format to another:

use kittycad::types::{FileExportFormat, FileImportFormat};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();
    let input = std::fs::read("part.step")?;

    let conversion = client
        .file()
        .create_conversion(
            FileExportFormat::Stl,
            FileImportFormat::Step,
            &bytes::Bytes::from(input),
        )
        .await?;

    println!("status: {:?}", conversion.status);
    Ok(())
}

See QUICKSTART.md for a full walkthrough and EXAMPLES.md for more worked examples.

What's in the box

Each area of the API is reached through a method on Client:

Accessor What it does
client.file() CAD file conversion, volume, mass, density, surface area, center of mass
client.ml() Text-to-CAD, KCL code completions, proprietary-format-to-KCL conversion
client.modeling() WebSocket connection to the Zoo modeling engine
client.executor() Remote code execution environments
client.unit() Unit conversions (length, mass, pressure, torque, …)
client.users() / client.orgs() Account, org and member management
client.api_tokens() / client.service_accounts() Credential management
client.api_calls() Query your API usage (debugging, billing)
client.payments() Billing and payment information
client.oauth2() OAuth 2.0 grant flows

Full per-method reference: docs.rs/kittycad.

Errors, retries, pagination

Contributing

The code in this repository is generated from spec.json — please don't edit it by hand (a bot closes such PRs). To regenerate locally, run make generate. Maintainer docs (releasing, CI smoke tests) live in MAINTAINERS.md (moved out of this README; see note in sample).

Support

License

MIT


File: QUICKSTART.md (raw markdown)

Quickstart: your first Zoo API call from Rust

This guide takes you from an empty directory to a successful API call in about five minutes.

1. Get an API token

  1. Sign in at zoo.dev.
  2. Open Account → API tokens and create a token.
  3. Export it (the client checks ZOO_API_TOKEN, then KITTYCAD_API_TOKEN):
export ZOO_API_TOKEN="your-token"

2. Create a project

cargo new zoo-hello && cd zoo-hello
cargo add kittycad anyhow
cargo add tokio --features rt,macros
cargo add bytes

3. Verify authentication

Replace src/main.rs with:

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();
    let me = client.users().get_self().await?;
    println!("authenticated as: {:?}", me);
    Ok(())
}
cargo run

If you see your account details, the token works. A 401-carrying Error::Server here means the token is missing or wrong.

4. Do something real: convert a CAD file

The file API converts between CAD formats server-side — no local CAD kernel needed. This converts a STEP file to STL:

use kittycad::types::{FileExportFormat, FileImportFormat};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();
    let input = std::fs::read("part.step")?;

    let conversion = client
        .file()
        .create_conversion(
            FileExportFormat::Stl,            // what you want
            FileImportFormat::Step,           // what you have
            &bytes::Bytes::from(input),       // raw file bytes
        )
        .await?;

    println!("conversion status: {:?}", conversion.status);
    Ok(())
}

Small conversions usually complete synchronously; the returned FileConversion carries the status, and completed output is returned base64-encoded in the response's outputs map.

5. Where to go next

Troubleshooting

Symptom Likely cause
Error::Server { status: 401, .. } Token missing/expired — re-check step 1
new_from_env() panics Neither ZOO_API_TOKEN nor KITTYCAD_API_TOKEN is set
Conversion returns an error status Source format doesn't match the bytes you sent

File: EXAMPLES.md (raw markdown)

Worked examples

All examples assume ZOO_API_TOKEN is exported and these dependencies:

[dependencies]
kittycad = "0.5.7"
tokio = { version = "1", features = ["rt", "macros"] }
anyhow = "1"
bytes = "1"
futures-util = "0.3"

1. Convert a CAD file (STEP → STL)

use kittycad::types::{FileExportFormat, FileImportFormat};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();
    let input = std::fs::read("part.step")?;

    let result = client
        .file()
        .create_conversion(
            FileExportFormat::Stl,
            FileImportFormat::Step,
            &bytes::Bytes::from(input),
        )
        .await?;

    println!("{:?}", result.status);
    Ok(())
}

The same module also computes geometry properties of an uploaded file: create_volume, create_mass, create_density, create_surface_area, create_center_of_mass, create_bounding_box.

2. Convert units

Server-side unit conversion across length, mass, force, pressure, torque, temperature, and more:

use kittycad::types::UnitLength;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();

    let conv = client
        .unit()
        .get_length_conversion(UnitLength::In, UnitLength::Mm, 2.5)
        .await?;

    println!("2.5 in = {:?} mm", conv.output);
    Ok(())
}

3. Inspect your account and API usage

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();

    let me = client.users().get_self().await?;
    println!("me: {:?}", me);
    Ok(())
}

client.api_calls() exposes your recent API calls — useful for debugging and for understanding billing.

4. Paginate with streams

Every list endpoint has a *_stream twin that handles page tokens for you and yields items as a futures::Stream. For example, listing your text-to-CAD generations:

use futures_util::TryStreamExt;
use kittycad::ml::ListTextToCadPartsForUserParams;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = kittycad::Client::new_from_env();
    let mut ml = client.ml();

    let mut stream = ml.list_text_to_cad_parts_for_user_stream(
        ListTextToCadPartsForUserParams {
            conversation_id: None,
            limit: None,
            no_models: None,
            no_parts: Some(true),   // metadata only
            page_token: None,
            sort_by: None,
        },
    );

    while let Some(part) = stream.try_next().await? {
        println!("{:?}", part);
    }
    Ok(())
}

5. Handling errors

Every call returns Result<T, kittycad::types::error::Error>. Server-side failures carry the status and raw body:

use kittycad::types::error::Error;

#[tokio::main]
async fn main() {
    let client = kittycad::Client::new_from_env();
    match client.users().get_self().await {
        Ok(me) => println!("ok: {:?}", me),
        Err(Error::Server { status, body }) => {
            eprintln!("API error {}: {}", status, body);
        }
        Err(other) => eprintln!("client-side error: {}", other),
    }
}

Transient network failures are retried automatically with backoff (reqwest-retry under the hood), so you generally only see errors that are worth surfacing.

← All samples · Packages & pricing