Skip to content

Elixir

Hex.pm Build Status License: MIT

Elixir SDK for the Poodle email sending API.

  • 🚀 Simple and intuitive API
  • 📧 HTML and plain text email support
  • 🔒 Comprehensive error handling
  • ⚡ Asynchronous email sending
  • 🛡️ Built-in input validation
  • 📊 Rate limiting support
  • 🔧 Configurable via environment variables
  • 🎯 Pattern-matchable error tuples
  • 📖 Complete documentation and examples
  • ✅ Elixir 1.14+ support

Add poodle to your list of dependencies in mix.exs:

def deps do
[
{:poodle, "~> 1.0"}
]
end

Then run:

Terminal window
mix deps.get
Terminal window
export POODLE_API_KEY=your_api_key_here
# Send an HTML email
{:ok, response} = Poodle.send_html(
"Hello from Poodle!",
"<h1>Welcome!</h1><p>This is a test email.</p>"
)
IO.puts("Email sent! Message: #{response.message}")

The SDK can be configured via environment variables or application config:

Terminal window
export POODLE_API_KEY=your_api_key
export POODLE_BASE_URL=https://api.usepoodle.com
export POODLE_TIMEOUT=30000
export POODLE_DEBUG=false
config/config.exs
config :poodle,
api_key: "your_api_key",
base_url: "https://api.usepoodle.com",
timeout: 30_000,
debug: false
# HTML email
{:ok, response} = Poodle.send_html(
"Welcome!",
"<h1>Hello World!</h1><p>Welcome to our service!</p>"
)
# Plain text email
{:ok, response} = Poodle.send_text(
"Welcome!",
"Hello World! Welcome to our service!"
)
# Both HTML and text
{:ok, response} = Poodle.send(
"Welcome!",
html: "<h1>Hello World!</h1>",
text: "Hello World!"
)
# Create an email struct
{:ok, email} = Poodle.Email.new(
"Welcome!",
html: "<h1>Hello World!</h1>",
text: "Hello World!"
)
# Send the email
{:ok, response} = Poodle.send_email(email)
# Send email asynchronously
task = Poodle.send_async(
"Welcome!",
html: "<h1>Hello World!</h1>"
)
# Wait for result
{:ok, response} = Task.await(task)
case Poodle.send_html(from, to, subject, html) do
{:ok, response} ->
IO.puts("Email sent! Message: #{response.message}")
# Check rate limit info
if response.rate_limit do
IO.puts("Rate limit remaining: #{response.rate_limit.remaining}")
end
{:error, %Poodle.Error{type: :unauthorized}} ->
IO.puts("Invalid API key")
{:error, %Poodle.Error{type: :rate_limit_exceeded, retry_after: retry_after}} ->
IO.puts("Rate limited. Retry after #{retry_after} seconds")
{:error, %Poodle.Error{type: :validation_error, message: message}} ->
IO.puts("Validation error: #{message}")
{:error, %Poodle.Error{type: :payment_required}} ->
IO.puts("Subscription expired or limit reached")
{:error, error} ->
IO.puts("Error: #{error.message}")
end
  • Poodle.send/4 - Send email with HTML and/or text content
  • Poodle.send_html/5 - Send HTML email
  • Poodle.send_text/5 - Send plain text email
  • Poodle.send_email/3 - Send email using Email struct
  • Poodle.send_async/4 - Send email asynchronously
  • Poodle.validate_config/1 - Validate configuration
%Poodle.Email{
subject: "Email Subject",
html: "<h1>HTML content</h1>", # optional
text: "Plain text content" # optional
}
%Poodle.Response{
success: true,
message: "Email queued for sending",
rate_limit: %Poodle.RateLimit{
limit: 2,
remaining: 1,
reset: 1640995200
}
}
%Poodle.Error{
type: :rate_limit_exceeded,
message: "Rate limit exceeded",
status_code: 429,
retry_after: 30,
details: %{...}
}

The SDK provides specific error types for different scenarios:

  • :validation_error - Invalid input data
  • :unauthorized - Invalid or missing API key
  • :forbidden - Account suspended
  • :payment_required - Subscription issues
  • :rate_limit_exceeded - Rate limit exceeded
  • :server_error - Server-side errors
  • :network_error - Network connectivity issues
  • :timeout - Request timeout
  • :dns_error - DNS resolution failed
  • :ssl_error - SSL/TLS errors
defmodule MyAppWeb.EmailController do
use MyAppWeb, :controller
def send_welcome(conn, %{"email" => email, "name" => name}) do
case Poodle.send_html(
email,
"Welcome, #{name}!",
render_welcome_email(name)
) do
{:ok, _response} ->
json(conn, %{success: true, message: "Welcome email sent"})
{:error, error} ->
conn
|> put_status(:unprocessable_entity)
|> json(%{success: false, error: error.message})
end
end
defp render_welcome_email(name) do
"""
<h1>Welcome, #{name}!</h1>
<p>Thank you for joining our service.</p>
"""
end
end
defmodule MyApp.Workers.EmailWorker do
use Oban.Worker, queue: :emails
@impl Oban.Worker
def perform(%Oban.Job{args: %{"type" => "welcome", "email" => email, "name" => name}}) do
case Poodle.send_html(
email,
"Welcome, #{name}!",
render_welcome_email(name)
) do
{:ok, _response} -> :ok
{:error, _error} -> {:error, "Failed to send email"}
end
end
defp render_welcome_email(name) do
"""
<h1>Welcome, #{name}!</h1>
<p>Thank you for joining our service.</p>
"""
end
end
Terminal window
# Set test environment variables
export POODLE_API_KEY=test_api_key
# Run tests
mix test
# Run tests with coverage
mix test --cover
Terminal window
# Format code
mix format
# Run Credo
mix credo
# Run Dialyzer
mix dialyzer

Contributions are welcome! Please read our Contributing Guide for details on the process for submitting pull requests and our Code of Conduct.

This project is licensed under the MIT License - see the LICENSE file for details.