Lettermint Elixir SDK
The official Elixir SDK for the Lettermint sending and team APIs.
Requirements
- Elixir 1.15 or later and a compatible Erlang/OTP release.
Installation
Before the first Hex release, install this package from GitHub.
Add this dependency to your application's mix.exs:
{:lettermint, github: "lettermint/lettermint-elixir", branch: "main"}
Run mix deps.get. The SDK uses Req for HTTP and Jason for JSON. No application configuration is required.
Usage
Sending Emails
Use a project token for email operations. Use a team token for management operations.
client = Lettermint.email(System.fetch_env!("LETTERMINT_PROJECT_TOKEN"))
{:ok, message} = Lettermint.Email.send(client, %{
from: "Example <sender@example.com>",
to: ["recipient@example.com"],
subject: "Hello",
text: "Hello from Elixir",
metadata: %{order_id: "123"},
tags: [%{name: "type", value: "receipt"}],
settings: %{tls: "enforced"}
}, idempotency_key: "receipt-123")
message.message_id
message.status
All API calls return {:ok, result} or {:error, %Lettermint.Error{}}. Invalid local arguments raise ArgumentError. JSON responses use generated structs. Raw message source, HTML, and text remain unchanged strings. Ping returns a trimmed string.
Using the Pipe Operator
alias Lettermint.EmailBuilder, as: Email
client
|> Email.new()
|> Email.from("sender@example.com")
|> Email.to(["recipient@example.com"])
|> Email.subject("Hello")
|> Email.text("Hello from Elixir")
|> Email.send(idempotency_key: "hello-123")
Each builder function replaces one field and returns a new builder. Use Email.to_map/1 to prepare a batch item.
Batch and Scheduled Messages
Lettermint.Email.send_batch(client, [
%{from: "sender@example.com", to: ["a@example.com"], subject: "First", text: "Hello"},
%{from: "sender@example.com", to: ["b@example.com"], subject: "Second", text: "Hello"}
], idempotency_key: "batch-123")
Lettermint.Email.send(client, %{
from: "sender@example.com", to: ["recipient@example.com"],
subject: "Scheduled", text: "Hello", scheduled_at: "tomorrow at 9am"
})
Lettermint.Messages.reschedule(client, "message-id", %{scheduled_at: "tomorrow at 10am"})
Lettermint.Messages.cancel(client, "message-id")
The API accepts up to 500 batch items. The request and response are bare arrays. Batch metadata and headers are string maps. Scheduling accepts an ISO 8601 timestamp or a supported English date expression. Use a time zone in absolute timestamps. The API uses UTC when no time zone is given. Reschedule and cancel accept either client type.
Team API
Use a team API token with Lettermint.api(...).
api = Lettermint.api(System.fetch_env!("LETTERMINT_TEAM_TOKEN"))
{:ok, page} = Lettermint.Domains.list(api, query: %{"page[size]" => 25})
if page.next_cursor not in [nil, :unset] do
Lettermint.Domains.list(api, query: %{"page[cursor]" => page.next_cursor, "page[size]" => 25})
end
Lettermint.Messages.list(api, query: %{"filter[status]" => "scheduled"})
Lettermint.Projects.retrieve(api, "project-id")
Lettermint.Routes.list(api, "project-id")
Lettermint.Stats.retrieve(api)
Lettermint.Team.retrieve(api)
Cursor pages have data, path, per_page, next_cursor, next_page_url, prev_cursor, and prev_page_url. Each item in data is a generated struct. The SDK makes one request per call. Pass query keys exactly as the API defines them; the SDK encodes them once.
All endpoint functions are listed in specs/operations.json. The modules are:
| Module | Operations |
|---|---|
Lettermint.Email | Send, batch send, ping |
Lettermint.API | Ping, blocked file types |
Lettermint.Domains | List, create, retrieve, delete, DNS verification, project assignment |
Lettermint.Messages | List, retrieve, events, source, HTML, text, reschedule, cancel, inbound processing |
Lettermint.Projects | List, create, retrieve, update, delete, token rotation |
Lettermint.Routes | List, create, retrieve, update, delete, inbound domain verification |
Lettermint.Stats | Statistics |
Lettermint.Suppressions | List, create, delete, removal review results |
Lettermint.Team | Retrieve, update, usage, roles, members, member assignment |
Lettermint.Webhooks | List, create, retrieve, update, delete, test, secret rotation, deliveries |
Responses and Models
payload = %Lettermint.Models.SendMailRequest{
from: "sender@example.com", to: ["recipient@example.com"],
subject: "Hello", text: "Hello"
}
case Lettermint.Email.send(client, payload) do
{:ok, result} -> Lettermint.Model.to_map(result)
{:error, %Lettermint.Error{kind: :api, status: status, body: body}} -> {status, body}
{:error, %Lettermint.Error{kind: kind}} -> kind
end
Request maps accept atom or string keys. Generated fields default to :unset, which omits the field from JSON. An explicit nil sends JSON null. Unknown response fields remain in the struct's extra map with string keys. Enum values remain strings, including new values from the API. Enum modules provide values/0. The decoder accepts an empty PHP array as an empty map only for map fields.
Error Handling
Error kinds are :api, :transport, and :decode. API errors include the HTTP status and response body. Token text is removed from error data. Client inspection does not show the token. Automatic retries and redirects are disabled. Set an idempotency key when your application can repeat a write request.
Configuration
client = Lettermint.email("project-token", timeout: 15_000)
Lettermint.Email.ping(client, headers: %{"x-request-id" => "trace-123"})
Client options: timeout in milliseconds, base_url (default https://api.lettermint.co/v1), and adapter (a module that implements Lettermint.Adapter). The timeout applies separately to connection setup and response receipt. Request options: query, headers, and idempotency_key. Request headers cannot replace authentication or HTTP routing headers. HTTPS uses the HTTP library's certificate verification.
Webhook Verification
Lettermint.Webhook.verify(raw_body, signature_header, webhook_secret)
Pass the original request body bytes before JSON decoding. The signature has the format t=timestamp,v1=hash. Verification returns :ok or {:error, :invalid_signature}. The default time tolerance is 300 seconds in either direction. Options now and tolerance use seconds.
Swoosh
Swoosh has an existing Lettermint adapter. Use that adapter for Swoosh mail delivery. This SDK supplies direct sending and management API access. It does not replace or redefine the Swoosh adapter.
Testing
mix deps.get
mix test --warnings-as-errors
Development
Code generation and API contract checks
mix deps.get
python3 tools/generate.py
python3 tools/generate.py --check
python3 -m unittest discover -s tools -p 'test_*.py'
mix format --check-formatted
mix compile --warnings-as-errors
mix test --warnings-as-errors
mix hex.audit
mix hex.build
The generator requires Python 3 and Elixir. Both API specifications are pinned in specs/. It does not use files from another SDK. It generates models, endpoint functions, and the operation manifest. Do not edit lib/lettermint/generated.ex by hand.
Tests use stored API contracts, synthetic fixtures, and a local HTTP server. They do not call the production API.
Changelog
See CHANGELOG.md for release changes.
Support
For help, join the Lettermint Discord server.
Credits
License
The MIT License (MIT). See LICENSE for details.