Rust SDK
Connect maxclicks to your Rust service with explicit request results and errors. The client covers customer data, events, prepared campaigns, and workflow triggers.
Minimum supported Rust version is 1.70.
Current API coverage
The SDK groups Public API calls by resource. Use the API reference for request fields, response payloads, and operation semantics, and the examples below for language-specific method calls.
Pagination helpers follow the API traversal limits. An iterator or method named all stops at the traversal cap; use scoped queries when working with larger datasets.
Install
Add the SDK to your Cargo.toml:
[dependencies]
maxclicks = "0.2.0"
Quickstart
Construct a Client with your API key, then call resource groups. Records and event inputs are flat and accept any Serialize value, including serde_json::json!.
use maxclicks::Client;
use serde_json::json;
fn main() -> Result<(), maxclicks::MaxclicksError> {
let client = Client::new("max_...")?;
// Identify the calling key.
let me = client.me()?;
println!("space: {:?}", me.space);
// Upsert a contact by identity. Arbitrary custom keys are preserved.
let contact = client.records().upsert(
"students",
&json!({ "email": "[email protected]", "firstName": "Ada", "plan": "pro" }),
None, // optional Idempotency-Key
)?;
println!("contact id: {}", contact.id);
// Walk every record across all pages.
for record in client.records().list_all("students") {
let record = record?;
println!("{}", record.id);
}
Ok(())
}
Client
Construct a client three ways.
| Constructor | Behavior |
|---|---|
Client::new("max_...") | Bare API key. |
Client::from_env() | Reads MAXCLICKS_API_KEY. |
Client::builder() | Full control over transport and headers. |
use maxclicks::Client;
use std::time::Duration;
let client = Client::builder()
.api_key("max_...")
.base_url("https://api.maxclicks.ai/v1")
.timeout(Duration::from_secs(30))
.max_retries(3)
.default_header("X-My-Trace", "abc")
.on_warning(|warnings, context| {
eprintln!("{} {}: {:?}", context.method, context.path, warnings);
})
.build()?;
- Name
api_key- Type
- string
- Description
Sent as
Authorization: Bearer <key>. Falls back toMAXCLICKS_API_KEY.
- Name
base_url- Type
- string
- Description
Defaults to
https://api.maxclicks.ai/v1. Trailing slashes are trimmed.
- Name
timeout- Type
- Duration
- Description
Per request. Default 60s.
- Name
max_retries- Type
- u32
- Description
Extra attempts on retryable failures. Default 2.
- Name
on_warning- Type
- callback
- Description
Invoked with API warnings per call.
- Name
transport- Type
- Arc<dyn Transport>
- Description
Defaults to blocking
reqwest. Inject a mock to test without a network.
Errors
Every call returns Result<T, MaxclicksError>. Match a variant, or use the uniform accessors error.status(), error.code(), and error.message().
use maxclicks::MaxclicksError;
match client.schemas().get("missing") {
Ok(schema) => println!("{}", schema.name),
Err(MaxclicksError::NotFound(data)) => eprintln!("no such schema: {}", data.message),
Err(MaxclicksError::RateLimit { retry_after, .. }) => {
eprintln!("slow down; retry after {:?}", retry_after);
}
Err(error) => eprintln!(
"status={:?} code={:?} message={}",
error.status(),
error.code(),
error.message(),
),
}
| Variant | Status | Notes |
|---|---|---|
BadRequest | 400 | |
Authentication | 401 | |
PaymentRequired | 402 | insufficient_credits on an AI or auto-fill call, email_limit_reached on a send. |
Permission | 403 | |
NotFound | 404 | |
Conflict | 409 | |
UnprocessableEntity | 422 | |
RateLimit | 429 | Carries retry_after. |
Server | 5xx | |
Api | other | 405/413/415 or an undecodable body. |
Connection | Transport failure. | |
Timeout | Request timed out. | |
Config | Programmer error such as a missing key, raised before any request. |
Retries and rate limits
Reads are retried on 429, any 5xx, and transport or timeout errors, up to max_retries extra attempts. A write is retried on those only when it carries an idempotency key; without one it is retried on 429 alone, because a 5xx or a timeout leaves the outcome unknown and a repeat could send twice. Backoff is exponential with full jitter, base 500ms, cap 8s. A Retry-After header (delay-seconds or HTTP date) sets the delay floor. Per-key limits are 100 req/s for reads and 25 req/s for writes.
Pagination
List endpoints return a Page<T> with data, pagination, and warnings. Every list method except workflows().runs has a *_all(...) variant returning a Paginator<T> that walks every page. It stops when has_more is false or at the offset cap of 10,000. Page workflows().runs yourself with its limit and offset arguments.
events().list_all(...) is the cursor-paginated exception. It returns a CursorPaginator<Event> driven by the opaque next_cursor rather than an offset, and takes the same from and to window as events().list.
// One page.
let page = client.records().list("students", Some(100), Some(0))?;
println!("{} of {}", page.data.len(), page.pagination.total_count);
// Every item, auto-fetching pages.
for record in client.records().list_all("students") {
let record = record?;
// ...
}
// Or collect all at once. Short-circuits on the first error.
let all = client.records().list_all("students").collect_all()?;
Idempotency
Writes such as records().create, records().upsert, suppressions().create_batch, suppressions().remove_batch, templates().send, broadcasts().send, and workflows().trigger take a trailing Option<&str> Idempotency-Key. Pass None to skip. See the supported API writes and retry contract before replaying an operation.
client.records().upsert(
"students",
&json!({ "email": "[email protected]", "firstName": "Ada" }),
Some("signup-2026-07-18-ada"),
)?;
Warnings
The API can attach warnings in the response body under warnings. For the empty-data workflows.trigger response, warnings arrive in the URL-encoded maxclicks-Warning-Message response header. Both surface on Page.warnings and through the on_warning callback.
Resource surface
Each group is a method on Client. Every list method also has an auto-paginating *_all(...) variant, with the single exception of workflows().runs.
| Group | Methods |
|---|---|
| root | me |
schemas() | list, get |
attributes() | list |
records() | create, upsert, list, get, update, delete, audit_trail |
suppressions() | create, create_batch, remove_batch, list, delete |
events() | fire, fire_batch, list |
domains() | list, get |
senders() | list |
topics() | list, get |
segments() | create, list, get, delete, count, contacts |
broadcasts() | list, get, update, send, runs, metrics |
templates() | list, get, send |
webhooks() | create, list, get, update, delete, rotate_secret |
workflows() | list, get, pause, unpause, runs, get_run, trigger |
forms() | submit, confirm_double_opt_in |
emails() | unsubscribe_one_click |
Create schemas, attributes, domains, senders, topics, templates and broadcasts in the maxclicks app, then use resource methods to read them or act on prepared work. Manage API keys and CSV imports in the app. See the API reference for request fields and response details.
forms.submit, forms.confirm_double_opt_in, and
emails.unsubscribe_one_click are unauthenticated. AI-backed calls
(segments().create and webhooks() writes carrying a custom-filter
condition) return PaymentRequired when the space is out of credits.
Retry outcomes
A repeated write can return a recorded result, including an error. If the API reports idempotency_outcome_unknown, inspect the affected resource or run history before creating a new operation. Keep the original key and payload while you reconcile. See the idempotency guide for supported operations and retention.