Linkit
SDK Reference

Rust SDK

High-performance async Rust SDK for the Linkit API — fluent builders, zero-cost abstractions, serde models, and built-in resilience.

Rust SDK

The Linkit Rust SDK provides a fully async, type-safe client for the Linkit e-commerce API. Built with reqwest, serde, and tokio, it delivers zero-cost abstractions with fluent builder APIs identical to the C# SDK architecture.

Async First

All operations are async and return Result<T, LinkitError>. The SDK integrates retry, circuit breaker, and rate limiting transparently.


Installation

# Cargo.toml
[dependencies]
linkit-sdk = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
# Download from Linkit docs downloads
tar xf linkit-sdk-0.1.0.crate
cd linkit-sdk-0.1.0

# Add as path dependency
[dependencies]
linkit-sdk = { path = "./linkit-sdk-0.1.0" }
[dependencies]
linkit-sdk = { git = "https://github.com/theHamdiz/linkit", branch = "main" }

Quick Start

use linkit_sdk::{LinkitClient, LinkitConfig};

#[tokio::main]
async fn main() -> linkit_sdk::Result<()> {
    // Create client with custom config
    let client = LinkitClient::with_config(
        LinkitConfig::builder()
            .base_url("https://linkit.works/api/v1")
            .timeout_secs(30)
            .build()?,
    )?
    .with_auth("your-jwt-token")
    .await?;

    // Health check
    let health = client.health().check().await?;
    println!("API status: {}", health.status);

    // Create a product
    let product = client.products().create()
        .iv_id("PROD-001")
        .name("Premium Coffee", "قهوة ممتازة")
        .price(29.99)
        .enabled(true)
        .send()
        .await?;
    println!("Created: {}", product.data.iv_id);

    Ok(())
}

Configuration

Presets

// Default — balanced settings
let client = LinkitClient::new()?;

// High throughput — 50 concurrent, 5 retries
let client = LinkitClient::with_config(LinkitConfig::high_throughput())?;

// Development — long timeout, minimal retries
let client = LinkitClient::with_config(LinkitConfig::development())?;

Custom Builder

use linkit_sdk::{LinkitConfig, RetryPolicy};
use std::time::Duration;

let config = LinkitConfig::builder()
    .base_url("https://api.yourcompany.com/v1")
    .timeout(Duration::from_secs(45))
    .max_concurrent_requests(30)
    .user_agent("my-app/1.0")
    .retry_policy(RetryPolicy {
        max_retries: 5,
        initial_delay: Duration::from_millis(100),
        backoff_multiplier: 2.0,
        max_delay: Duration::from_secs(30),
    })
    .default_header("X-Custom-Header", "value")
    .build()?;

Products

Create

let product = client.products().create()
    .iv_id("PROD-001")
    .name("Premium Coffee", "قهوة ممتازة")
    .description("Single origin beans", "حبوب أحادية المصدر")
    .price(29.99)
    .price_after_vat(34.49)
    .vat(15.0)
    .category_id("beverages")
    .brand_id("brand-01")
    .barcode("6281234567890")
    .enabled(true)
    .fast_moving(true)
    .quick_commerce(true)
    .shippable(true)
    .refrigerated(false)
    .hero_image("https://cdn.example.com/coffee.jpg")
    .buffer(10.0)
    .send()
    .await?;

List with Filters

let products = client.products().list()
    .page(1)
    .limit(50)
    .enabled(true)
    .category("beverages")
    .brand("brand-01")
    .search("coffee")
    .fast_moving(true)
    .send()
    .await?;

for p in &products.data {
    println!("{}: {}", p.iv_id, p.name_en.as_deref().unwrap_or(""));
}
println!("Total: {} | Has next: {}", products.total_count, products.has_next);

Get / Update / Delete

// Get
let product = client.products().get("PROD-001").await?;

// Update
let updated = client.products().update("PROD-001")
    .price(24.99)
    .enabled(false)
    .send()
    .await?;

// Delete
let result = client.products().delete("PROD-001").await?;

Branches

Create

let branch = client.branches().create()
    .iv_id("BR-RYD-001")
    .name("Riyadh Central", "الرياض سنتر")
    .location(24.7136, 46.6753)
    .active(true)
    .email("riyadh@example.com")
    .google_maps_id("ChIJ...")
    .send()
    .await?;
let nearby = client.branches().list()
    .near(24.7136, 46.6753, 5.0) // 5km radius
    .active(true)
    .sort("-created")
    .send()
    .await?;

SKUs

Create & Update Stock

// Create SKU
let sku = client.skus().create()
    .iv_id("SKU-001")
    .branch_iv_id("BR-RYD-001")
    .product_iv_id("PROD-001")
    .price(29.99)
    .qty(1000.0)
    .max_qty(5000.0)
    .available(true)
    .barcode("6281234567890")
    .amazon_sku("AMZN-001")
    .salla_id("SL-001")
    .send()
    .await?;

// Update stock
let stock = client.skus().update_stock("SKU-001", "BR-RYD-001")
    .qty(950.0)
    .available(true)
    .send()
    .await?;

Customers

// Create
let customer = client.customers().create()
    .first_name("Ahmad")
    .last_name("Hamdi")
    .email("ahmad@example.com")
    .phone("+966500000000")
    .customer_type("individual")
    .send()
    .await?;

// Lookup
let found = client.customers().find_by_email("ahmad@example.com").await?;
println!("Customer: {} (active: {})", found.full_name(), found.is_active());

Brands

// Create
let brand = client.brands().create()
    .brand_code("BRD-01")
    .name("Acme Corp", "أكمي")
    .logo_url("https://cdn.example.com/acme.png")
    .send()
    .await?;

// List
let brands = client.brands().list()
    .page(1).limit(25).search("Acme")
    .send().await?;
for b in &brands.data {
    println!("{}: {}", b.brand_code, b.name_en.as_deref().unwrap_or(""));
}

// Update / Delete
let updated = client.brands().update("BRD-01").name("Acme Inc", "أكمي إنك").send().await?;
client.brands().delete("BRD-01").await?;

Categories

// Create with parent hierarchy
let category = client.categories().create()
    .code("CAT-BEV")
    .name("Beverages", "مشروبات")
    .parent("CAT-ROOT")
    .sort_order(1)
    .send()
    .await?;

// List with parent filter
let children = client.categories().list()
    .parent("CAT-ROOT")
    .page(1).limit(50)
    .send().await?;
for c in &children.data {
    println!("{}: {} (depth: {})", c.code, c.name_en.as_deref().unwrap_or(""), c.depth);
}

Orders

// List with filters
let orders = client.orders().list()
    .status("confirmed")
    .payment_status("completed")
    .from("2026-01-01")
    .to("2026-03-26")
    .currency("SAR")
    .sort("-created")
    .page(1).limit(20)
    .send()
    .await?;

for order in &orders.data {
    println!("Order {} — {} {} ({})",
        order.id,
        order.total_amount.unwrap_or(0.0),
        order.currency.as_deref().unwrap_or("SAR"),
        order.order_status.as_deref().unwrap_or("unknown"),
    );
}

// Update status
let updated = client.orders().update_status("ORD-001")
    .order_status("shipped")
    .fulfillment_status("shipped")
    .notes("Dispatched via Aramex")
    .send()
    .await?;

Offers

// Create a direct discount offer
let offer = client.offers().create()
    .name("Weekend 15%")
    .offer_type("direct_discount")
    .status("active")
    .target_scope("product")
    .target_iv_id("PRD-1001")
    .discount_type("percentage")
    .discount_value(15.0)
    .start_at("2026-03-25T00:00:00Z")
    .end_at("2026-03-31T23:59:59Z")
    .send()
    .await?;
println!("Created offer: {}", offer.data.id);

// List with filters
let offers = client.offers().list()
    .status("active")
    .offer_type("direct_discount")
    .is_active(true)
    .page(1).limit(20)
    .sort("-created")
    .send()
    .await?;

for o in &offers.data {
    println!("{}: {} ({})", o.id, o.name, o.offer_type);
}

// Update status
client.offers().update_status("ofr_123")
    .status("paused")
    .active(false)
    .send()
    .await?;

// Bulk upsert
use linkit_sdk::models::offer::OfferRequest;
let bulk = client.offers().bulk_upsert(vec![
    OfferRequest { name: "BOGO".into(), offer_type: "bogo".into(), buy_qty: 1, get_qty: 1, ..Default::default() },
    OfferRequest { name: "Bundle".into(), offer_type: "bundle_deal".into(), ..Default::default() },
]).await?;
println!("Processed: {}, Failed: {}", bulk.data.processed, bulk.data.failed);

// Delete
client.offers().delete("ofr_123").await?;

Generics

// Create
let generic = client.generics().create()
    .generic_code("GEN-PARA")
    .name("Paracetamol", "باراسيتامول")
    .send()
    .await?;

// List
let generics = client.generics().list()
    .page(1).limit(50).search("para")
    .send().await?;

Integrations

// List all integrations
let integrations = client.integrations().list().await?;
for i in &integrations {
    println!("{}: {} (enabled: {})", i.slug.as_deref().unwrap_or(""), i.name.as_deref().unwrap_or(""), i.enabled);
}

// Get config, enable, execute
let config = client.integrations().get_config("hungerstation").await?;
client.integrations().enable("hungerstation").await?;
let result = client.integrations().execute("hungerstation").await?;
println!("Execution: {} ({})", result.message, if result.success { "✓" } else { "✗" });
client.integrations().disable("hungerstation").await?;

Error Handling

use linkit_sdk::LinkitError;

match client.products().get("PROD-XXX").await {
    Ok(product) => println!("Found: {}", product.iv_id),
    Err(LinkitError::Api { status: 404, .. }) => println!("Product not found"),
    Err(LinkitError::Api { status, message, .. }) => {
        eprintln!("API error {status}: {message}");
    }
    Err(LinkitError::CircuitOpen) => {
        eprintln!("Service temporarily unavailable");
    }
    Err(LinkitError::Timeout { elapsed_ms }) => {
        eprintln!("Timed out after {elapsed_ms}ms");
    }
    Err(e) => eprintln!("Unexpected: {e}"),
}

Resilience Pipeline

Every request automatically flows through a 3-layer resilience pipeline:

LayerDefaultDescription
Rate Limiter10 concurrentToken-bucket semaphore via tokio::sync::Semaphore
Circuit Breaker5 failures / 60s resetLock-free atomics, half-open recovery
Retry3 attempts, 200ms initialExponential backoff with capped delay

All transient errors (5xx, 429, timeouts, connection errors) are automatically retried. Non-transient errors (4xx, validation) propagate immediately.


Performance

The SDK is optimised for maximum throughput:

  • Release profile: LTO (fat), single codegen unit, stripped symbols, abort on panic
  • Zero-copy serde: #[serde(skip_serializing_if)] minimises payload size
  • Connection pooling: reqwest connection reuse with configurable pool size
  • Lock-free circuit breaker: Uses AtomicU32 / AtomicU64 — no mutex contention
  • Token-bucket rate limiter: tokio::Semaphore for backpressure without blocking