The Elixir SDK for the Guava voice-agent platform.
Two layers:
Guava.Client— account-level HTTP operations (phone numbers, SMS, outbound-call creation, campaigns).Guava.Agent— a behaviour for handling live voice calls. Define an agent module, then attach it to a channel withGuava.Channel(supervised) or the blocking helpers here (listen_phone/3,call_phone/5, …) for scripts.
See the README and the guides under docs/.
Summary
Types
An E.164 formatted phone number, e.g. "+14155550123".
Functions
Serve an outbound campaign by code. Blocks.
Place an outbound call and handle it with agent. Blocks until the call ends.
Stop accepting new calls and wait for the calls in progress to finish.
Listen for inbound phone calls on agent_number. Blocks.
Listen for inbound SIP calls on sip_code. Blocks.
Listen for inbound WebRTC calls (creates a code when nil). Blocks.
Whether every channel on this node has started listening and none is draining.
Start one or more Guava.Channel child specs under a supervisor and block
until they stop. For scripts/mix run; in an app, add the child specs to your
own supervision tree instead.
Types
@type phone_number() :: String.t()
An E.164 formatted phone number, e.g. "+14155550123".
Functions
Serve an outbound campaign by code. Blocks.
@spec call_phone(module(), phone_number(), phone_number(), map(), keyword()) :: :ok
Place an outbound call and handle it with agent. Blocks until the call ends.
@spec drain(keyword()) :: :ok | {:timeout, pos_integer()}
Stop accepting new calls and wait for the calls in progress to finish.
Every channel closes its listener socket, so the server stops assigning work
to this node; calls already running are untouched, since each owns a separate
socket. Returns :ok once the last call ends, or {:timeout, n} with the
number still running when the budget ran out.
This runs automatically when the :guava application stops, so a SIGTERM
during a rolling deploy no longer drops live calls. Call it yourself to drain
early — from a Kubernetes preStop hook, say.
The budget defaults to config :guava, drain_timeout: 30_000 and can be
overridden per call with :timeout. Keep it comfortably below your
orchestrator's kill deadline (terminationGracePeriodSeconds on Kubernetes),
which bounds the whole shutdown regardless.
@spec listen_phone(module(), phone_number(), keyword()) :: :ok
Listen for inbound phone calls on agent_number. Blocks.
Listen for inbound SIP calls on sip_code. Blocks.
Listen for inbound WebRTC calls (creates a code when nil). Blocks.
@spec ready?() :: boolean()
Whether every channel on this node has started listening and none is draining.
Intended for a readiness probe — wire it into your own endpoint, e.g. a Plug returning 200 when this is true and 503 otherwise. The SDK doesn't serve HTTP itself; see the deployment guide.
This is a startup and rollout gate, not a liveness signal: it stays true across transient reconnects, which the socket layer handles with buffering, so a network blip doesn't pull the node out of rotation.
@spec run(Supervisor.child_spec() | [Supervisor.child_spec()]) :: :ok
Start one or more Guava.Channel child specs under a supervisor and block
until they stop. For scripts/mix run; in an app, add the child specs to your
own supervision tree instead.
Guava.run({Guava.Channel, agent: MyAgent, listen: {:phone, "+14155550123"}})This is for channels that run indefinitely — listeners and campaigns. It waits on
the supervisor it creates, which stays up even after a finite channel has finished,
so it does not return when an outbound: channel's call ends. Use
call_phone/5 to place one outbound call and block until it finishes.