ConvergerClient
Elixir client for Converger, for Elixir apps that integrate with or embed
Converger: a REST client for the client API and the tenant API (Req), a
Protocol v1 WebSocket client with resume (Mint.WebSocket), and
outbound webhook signature verification.
def deps do
[{:converger_client, "~> 0.1"}]
end
Full documentation: converger.aimtune.dev/sdk/elixir.
REST
client = ConvergerClient.new("https://chat.example.com", api_key: "cvg_live_...", channel_secret: "...")
{:ok, token} = ConvergerClient.ClientAPI.generate_token(client, user_id: "user-42")
{:ok, conv} = ConvergerClient.ClientAPI.create_conversation(client, token.token)
{:ok, _} = ConvergerClient.ClientAPI.post_activity(client, conv.token, conv.conversation_id, %{text: "Hi!"},
idempotency_key: "msg-1")
{:ok, reply} = ConvergerClient.TenantAPI.post_activity(client, conv.conversation_id, %{sender: "bot", text: "Hello!"})
Errors are {:error, %ConvergerClient.Error{status, code, message, details, request_id, retry_after}}, the same
whatever error shape the server used.
WebSocket (Protocol v1)
{:ok, sock} =
ConvergerClient.Socket.start_link(client: client, token: conv.token, conversation_id: conv.conversation_id)
{:ok, %ConvergerClient.Ack{seq: seq}} = ConvergerClient.Socket.send_text(sock, "Where is my order?")
receive do
{:converger, ^sock, %{"type" => "text", "data" => %{"text" => text}}} -> IO.puts(text)
{:converger_closed, ^sock, %{code: code}} -> IO.puts("closed #{code}")
end
Frames arrive in the owner's mailbox: persistent frames in seq order, exactly once. The socket reconnects with
jittered backoff, resumes from its watermark (:watermark to resume a stored position), re-sends unacknowledged
sends with their original clientId and answers gaps with sync. Pass a 0-arity function as :token to fetch
a fresh token on every connect.
Webhook signature verification
# In a Plug or Phoenix controller, with the raw body kept by a body reader:
case ConvergerClient.Webhook.verify_signature(conn.req_headers, raw_body, secret) do
:ok -> handle(Jason.decode!(raw_body))
{:error, reason} -> send_resp(conn, 401, Atom.to_string(reason))
end
secret may be a list while you rotate secrets; tolerance: changes the 300 s window.
Development
mix test # unit + conformance
CONVERGER_FIXTURES=../conformance/integration/fixtures.json mix test --only integration
Releases are tagged client-elixir/vX.Y.Z and published to Hex by .github/workflows/sdk-elixir-release.yml.