# Quickstart

Two snippets — a backend service signing requests, and a desktop app driving loopback OAuth.

## Backend service

```rust
use akira_billing::{Client, types::OtpVerifyPayload};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut client = Client::new(
        std::env::var("AKIRA_BILLING_URL")?,
        "unified-dev",
        std::env::var("AKIRA_BILLING_SECRET")?,
    );

    let res = client
        .verify_otp(OtpVerifyPayload {
            email: "user@example.com",
            code: "123456",
            device_fp: None,
        })
        .await?;

    // `verify_otp` stored the bearer on the client; subsequent calls are authenticated.
    let me = client.customer_me().await?;
    println!("signed in as {}", me.email);

    Ok(())
}
```

`Client::new` defaults to a fresh `reqwest::Client`. Reuse the same `Client` across requests in long-lived processes (it owns an internal connection pool).

## Desktop loopback OAuth

```toml
[dependencies]
akira-billing = { version = "0.3", features = ["loopback", "desktop"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

```rust
use akira_billing::{
    Client,
    loopback::{loopback_login, LoopbackOptions},
    desktop::browser::open_browser,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut client = Client::new(
        std::env::var("AKIRA_BILLING_URL")?,
        "unified-dev",
        std::env::var("AKIRA_BILLING_SECRET")?,
    );

    let outcome = loopback_login(
        &mut client,
        LoopbackOptions {
            provider: "github",
            product: "unified-dev",
            open_browser: |url| Box::pin(async move { open_browser(&url).await }),
            timeout: std::time::Duration::from_secs(300),
        },
    )
    .await?;

    println!("signed in as {}", outcome.exchange.customer.email);
    Ok(())
}
```

The closure passed to `open_browser` decides how the URL reaches the user — `open_browser` from the `desktop` module shells out to `open` / `xdg-open` / `rundll32`. For Tauri / Electron, replace with the platform-specific helper.

## License runtime

```rust
use akira_billing::{decode_license, verify_license, compute_remaining};
use akira_billing::types::LicenseRefreshPayload;

let res = client
    .license_refresh(LicenseRefreshPayload {
        product: "unified-dev",
        fingerprint: &fingerprint_hex,
    })
    .await?;

let decoded = decode_license(&res.license)?;
let pub_keys = client.public_license_keys().await?;
let active = pub_keys
    .keys
    .iter()
    .find(|k| k.key_id == res.license.key_id)
    .ok_or(anyhow::anyhow!("no public key for license"))?;

if !verify_license(&res.license, &active.public_key_base64)? {
    return Err(anyhow::anyhow!("forged license"));
}

let remaining = compute_remaining(&decoded.payload, "agent_run", local_consumed);
if remaining == Some(0) {
    return Err(anyhow::anyhow!("quota reached"));
}
```

## What to read next

- [10-client](10-client.md) — full `Client` method table
- [03-architecture](03-architecture.md) — feature flags, runtime targets
- [31-gate](31-gate.md) — `Gate` runtime that wraps the whole license check

---

Navigation: [← Installation](01-installation.md) · **Quickstart** · [Architecture →](03-architecture.md)
