loopengine 1.1.0

Rust client for the LoopEngine Ingest API
Documentation

LoopEngine Rust SDK

Rust client for the LoopEngine Ingest API. Create a client with your credentials, then call send with your payload.

Requirements: Rust 1.75+ (async/await, impl Trait)

Installation

Add to your Cargo.toml:

[dependencies]
loopengine = "0.1"
tokio = { version = "1", features = ["full"] }
serde_json = "1"

Usage

use loopengine::Client;

#[tokio::main]
async fn main() {
    let client = Client::new(
        std::env::var("LOOPENGINE_PROJECT_KEY").unwrap(),
        std::env::var("LOOPENGINE_PROJECT_SECRET").unwrap(),
        std::env::var("LOOPENGINE_PROJECT_ID").unwrap(),
    )
    .unwrap();

    client
        .send(serde_json::json!({"message": "User feedback here"}))
        .await
        .unwrap();
}
  • Client::new(key, secret, project_id) — Builds a client. Use your project key, secret, and project ID from the LoopEngine dashboard. Returns an error if any credential is empty.
  • client.send(payload).await — Sends the payload to the Ingest API at api.loopengine.dev. project_id is added automatically. The payload must match the fields and constraints configured for your project in the LoopEngine dashboard (e.g. required fields, allowed keys, value types). You can pass any value that implements serde::Serialize and serializes to a JSON object (a serde_json::json! map, a struct with #[derive(Serialize)], etc.). Use client.send_with_geo(payload, lat, lon).await to send device coordinates (see Geolocation below).

The client is safe for concurrent use — it wraps a reqwest::Client which maintains an internal connection pool.

Geolocation

You can send device location so feedback is associated with coordinates instead of IP-based geo. Use send_with_geo and pass Some(lat) and Some(lon). When both are provided, the SDK adds geo_lat and geo_lon to the request body; they are included in the HMAC signature. Pass None for both to use IP-based geolocation (or use send(payload)). Valid ranges: latitude -90 to 90, longitude -180 to 180.

// Without geo (IP-based location is used)
client.send(serde_json::json!({"message": "Feedback"})).await?;

// With device coordinates
client
    .send_with_geo(
        serde_json::json!({"message": "Bug at my location"}),
        Some(34.05),
        Some(-118.25),
    )
    .await?;

Custom HTTP client

Use Client::builder to pass a custom reqwest::Client (e.g. to set timeouts or a custom connector):

use std::time::Duration;

let http = reqwest::Client::builder()
    .timeout(Duration::from_secs(10))
    .build()
    .unwrap();

let client = loopengine::Client::builder(key, secret, project_id)
    .with_http_client(http)
    .build()
    .unwrap();

Error handling

All errors are represented by loopengine::Error:

Variant When
MissingCredentials A credential is empty after trimming
Serialize Payload could not be serialized to JSON
Http Network / transport error
ApiError { status, body } Server returned a non-2xx response

Request signing

Every request is signed with HMAC-SHA256 over the canonical string "METHOD\nPATH\nTIMESTAMP\nSHA256(body)". The signature is base64url-encoded (no padding) and sent as the X-Signature: v1=<sig> header alongside X-Project-Key and X-Timestamp. All signing logic is handled transparently by the SDK.

License

MIT