Elixir SDK
Connect your Elixir application to the customer experiences your team builds in maxclicks. The client wraps the Public API with resource modules, tagged results, and structured errors.
Functions return {:ok, value} or {:error, %Maxclicks.Error{}}. Each has a ! sibling that returns the value or raises.
The Hex release is pending. The reference below describes the client interface; follow the HTTP quickstart for an integration you can run now.
Current API coverage
This guide documents the existing source client interface. The current REST API also exposes event readiness, event identity changes, record deletion status, and workflow history cursors that this client does not yet wrap. Event receipts and deletion results have changed; older static models may omit fields or expect the former { id, deleted } shape.
Use direct HTTP for those operations and verify the current coverage table before adopting a wrapper. Auto-pagination still stops at the API's traversal cap; an iterator or method named all does not guarantee a full workspace export.
Install
The Elixir SDK has not had its first release yet. It is not on Hex, so mix deps.get cannot resolve :maxclicks. Use the Public API directly until the release ships.
The reference below documents the shape of the client for when it does. Once installed, the SDK starts its own Finch pool (Maxclicks.Finch), so you add nothing to your supervision tree.
Client
Build a client with Maxclicks.new/1. The API key falls back to the MAXCLICKS_API_KEY environment variable when :api_key is not given.
client = Maxclicks.new(api_key: "max_...")
# Identify the calling key
{:ok, me} = Maxclicks.me(client)
# Upsert a contact by identity; input is a plain map of attribute values
{:ok, contact} =
Maxclicks.Records.upsert(client, "students", %{
"email" => "[email protected]",
"firstName" => "Ada",
"loyaltyTier" => "gold"
})
# Custom attributes are preserved and readable
Maxclicks.Model.Record.get_string(contact, "loyaltyTier")
# Send a template
{:ok, result} =
Maxclicks.Templates.send(client, template_id, %{"contact" => %{"email" => "[email protected]"}})
Options
- Name
api_key- Type
- string
- Description
Your API key. Defaults to
MAXCLICKS_API_KEY. Base URL ishttps://api.maxclicks.ai/v1.
- Name
base_url- Type
- string
- Description
Override the base URL. Use
Maxclicks.Client.stage_base_url()for staging.
- Name
timeout- Type
- integer
- Description
Per-request timeout in milliseconds.
- Name
max_retries- Type
- integer
- Description
Extra attempts on
429and5xxfor reads and for writes that pass:idempotency_key. A keyless write is never replayed, since it may already have landed. Retries use full-jitter backoff and honorRetry-After.
- Name
default_headers- Type
- map
- Description
Headers sent with every request.
- Name
on_warning- Type
- function
- Description
Callback
fn warnings, %{method: method, path: path} -> ... endinvoked on non-fatal API warnings.
- Name
transport- Type
- tuple
- Description
Swappable HTTP transport implementing the
Maxclicks.Transportbehaviour. Defaults to Finch.
Resources
Each API group is a module on the client.
Maxclicks.Schemas, Maxclicks.Attributes, Maxclicks.Records, Maxclicks.Suppressions, Maxclicks.Events, Maxclicks.Domains, Maxclicks.Senders, Maxclicks.Topics, Maxclicks.Segments, Maxclicks.Broadcasts, Maxclicks.Templates, Maxclicks.Webhooks, Maxclicks.Workflows, Maxclicks.Forms, Maxclicks.Emails, and Maxclicks.me/1.
Errors
Maxclicks.Error carries a type plus status, code, message, headers, raw, and retry_after. Match on type to branch.
case Maxclicks.Records.get(client, "students", id) do
{:ok, record} -> record
{:error, %Maxclicks.Error{type: :not_found}} -> nil
{:error, error} -> raise error
end
# Or use the raising variant
record = Maxclicks.Records.get!(client, "students", id)
type | Meaning |
|---|---|
:bad_request | 400 malformed request. |
:authentication | 401 missing or invalid API key. |
:permission | 403 key lacks permission. |
:not_found | 404 resource does not exist. |
:conflict | 409 identity or state conflict. |
:unprocessable_entity | 422 validation failure. |
:rate_limit | 429; retry_after is set. |
:server | 5xx server error. |
:api | API returned an unexpected shape. |
:connection | Transport could not connect. |
:timeout | Request exceeded timeout. |
:configuration | Client is misconfigured. |
Pagination
List endpoints return a Maxclicks.Page, or a Maxclicks.CursorPage for events.
{:ok, page} = Maxclicks.Records.list(client, "students", limit: 100)
page.data
page.pagination.total_count
case Maxclicks.Page.next_page(page) do
{:ok, next} -> next
:end -> :done
end
Iterate every item lazily with stream/*, which pages automatically.
client
|> Maxclicks.Records.stream("students")
|> Stream.filter(& &1.email)
|> Enum.take(500)
Warnings
The API surfaces non-fatal warnings, such as a clamped limit. Read them from page.warnings, or pass an :on_warning callback to observe every warning.
Testing without a network
The Maxclicks.Transport behaviour is the seam. Implement it in your own test support module and the SDK never touches the network. The stub the SDK uses for its own suite is not shipped in the package.
defmodule MyApp.StubTransport do
@behaviour Maxclicks.Transport
@impl true
def request(_request, _opts) do
{:ok,
%{
status: 200,
headers: %{"content-type" => "application/json"},
body: ~s({"data": {}}),
final_url: "https://api.maxclicks.ai/v1/resource"
}}
end
end
client =
Maxclicks.new(
api_key: "max_test",
transport: MyApp.StubTransport,
sleep_fun: fn _ -> :ok end
)
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.