Packages
phoenix_kit
1.7.49
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit/aws/sesv2.ex
defmodule PhoenixKit.AWS.SESv2 do
@moduledoc """
AWS SES v2 API client for operations not supported by ExAws.
This module provides direct API access to AWS SES v2 features that are not yet
available in the ExAws library, enabling infrastructure setup without AWS CLI dependency.
## Features
- Configuration set creation and management
- Event destination configuration for email tracking
- Automatic handling of "already exists" scenarios
- Production-ready error messages
## Usage
config = [
access_key_id: "AKIA...",
secret_access_key: "...",
region: "eu-north-1"
]
# Create configuration set
{:ok, name} = SESv2.create_configuration_set("myapp-emailing", config)
# Configure event tracking
:ok = SESv2.create_configuration_set_event_destination(
"myapp-emailing",
"email-events-to-sns",
"arn:aws:sns:eu-north-1:123456:topic",
config
)
"""
require Logger
@dialyzer {:nowarn_function,
create_configuration_set: 2, create_configuration_set_event_destination: 4}
@doc """
Creates a SES configuration set.
Configuration sets allow you to publish email sending events to Amazon SNS, CloudWatch, or Kinesis Firehose.
## Parameters
- `name` - Name of the configuration set (string)
- `config` - AWS configuration keyword list with:
- `:access_key_id` - AWS access key ID
- `:secret_access_key` - AWS secret access key
- `:region` - AWS region (e.g., "eu-north-1")
## Returns
- `{:ok, name}` - Configuration set created or already exists
- `{:error, reason}` - Failed to create configuration set
## Examples
iex> config = [access_key_id: "AKIA...", secret_access_key: "...", region: "eu-north-1"]
iex> SESv2.create_configuration_set("myapp-emailing", config)
{:ok, "myapp-emailing"}
# If configuration set already exists, returns success
iex> SESv2.create_configuration_set("myapp-emailing", config)
{:ok, "myapp-emailing"}
"""
def create_configuration_set(name, config) do
# Use ExAws.Operation.JSON for SES v2 API
# IMPORTANT: service must be :ses (not :email) for correct AWS signature
request = %ExAws.Operation.JSON{
http_method: :post,
service: :ses,
path: "/v2/email/configuration-sets",
data: %{"ConfigurationSetName" => name},
headers: [
{"content-type", "application/json"}
]
}
case ExAws.request(request, config) do
{:ok, _} ->
{:ok, name}
{:error, {:http_error, 409, _}} ->
# Already exists - this is fine
{:ok, name}
{:error, {:http_error, _code, %{body: body}}} when is_binary(body) ->
case Jason.decode(body) do
{:ok, %{"__type" => "AlreadyExistsException"}} ->
{:ok, name}
{:ok, %{"message" => msg}} ->
# Check if it's an "already exists" message (AWS may return this without AlreadyExistsException type)
if String.contains?(msg, "already exists") do
{:ok, name}
else
{:error, msg}
end
_ ->
{:error, body}
end
{:error, reason} ->
{:error, inspect(reason)}
end
end
@doc """
Creates an event destination for a configuration set.
Event destinations define where SES publishes email sending events (send, delivery, bounce, etc.).
## Parameters
- `config_set_name` - Name of the configuration set
- `destination_name` - Name for the event destination (e.g., "email-events-to-sns")
- `topic_arn` - SNS topic ARN where events will be published
- `config` - AWS configuration keyword list
## Returns
- `:ok` - Event destination created or already exists
- `{:error, reason}` - Failed to create event destination
## Events Tracked
The event destination is configured to track all 10 AWS SES email event types:
- `SEND` - Email accepted by AWS SES
- `REJECT` - Email rejected before sending
- `BOUNCE` - Email bounced (hard or soft)
- `COMPLAINT` - Recipient marked email as spam
- `DELIVERY` - Email successfully delivered
- `OPEN` - Recipient opened email (tracking pixel loaded)
- `CLICK` - Recipient clicked link in email
- `RENDERING_FAILURE` - Email template failed to render
- `DELIVERY_DELAY` - Temporary delivery delay occurred
- `SUBSCRIPTION` - Recipient updated subscription preferences or unsubscribed
## Examples
iex> topic_arn = "arn:aws:sns:eu-north-1:123456:myapp-email-events"
iex> SESv2.create_configuration_set_event_destination(
...> "myapp-emailing",
...> "email-events-to-sns",
...> topic_arn,
...> config
...> )
:ok
"""
def create_configuration_set_event_destination(
config_set_name,
destination_name,
topic_arn,
config
) do
data = %{
"EventDestinationName" => destination_name,
"EventDestination" => %{
"Enabled" => true,
"MatchingEventTypes" => [
"SEND",
"REJECT",
"BOUNCE",
"COMPLAINT",
"DELIVERY",
"OPEN",
"CLICK",
"RENDERING_FAILURE",
"DELIVERY_DELAY",
"SUBSCRIPTION"
],
"SnsDestination" => %{
"TopicArn" => topic_arn
}
}
}
request = %ExAws.Operation.JSON{
http_method: :post,
service: :ses,
path: "/v2/email/configuration-sets/#{URI.encode(config_set_name)}/event-destinations",
data: data,
headers: [
{"content-type", "application/json"}
]
}
case ExAws.request(request, config) do
{:ok, _} ->
:ok
{:error, {:http_error, 409, _}} ->
# Already exists - this is fine
:ok
{:error, {:http_error, _code, %{body: body}}} when is_binary(body) ->
case Jason.decode(body) do
{:ok, %{"__type" => "AlreadyExistsException"}} ->
:ok
{:ok, %{"message" => msg}} ->
# Check if message indicates resource already exists
if String.contains?(msg, "already exists"), do: :ok, else: {:error, msg}
_ ->
{:error, body}
end
{:error, reason} ->
{:error, inspect(reason)}
end
end
end