Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions sections/inbound-email/_intro.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Inbound Email

Create and reply to tickets directly from incoming emails. Escalated supports **Postmark** and **Mailgun** webhooks out of the box, with an open parser interface for plugging in additional providers.
Create and reply to tickets directly from incoming emails. Escalated supports **Postmark**, **Mailgun**, and **AWS SES** (via SNS HTTP subscription) out of the box, with an open parser interface for plugging in additional providers.

## How It Works

Expand All @@ -15,11 +15,11 @@ All greenfield framework ports (.NET, Spring, Go, Phoenix, Symfony) expose a **s

| Framework | Webhook URL |
| --- | --- |
| .NET | `POST /support/webhook/email/inbound?adapter=<postmark\|mailgun>` |
| Spring Boot | `POST /escalated/webhook/email/inbound?adapter=<postmark\|mailgun>` |
| Go | `POST /escalated/webhook/email/inbound?adapter=<postmark\|mailgun>` |
| Phoenix | `POST /support/webhook/email/inbound?adapter=<postmark\|mailgun>` |
| Symfony | `POST /escalated/webhook/email/inbound?adapter=<postmark\|mailgun>` |
| .NET | `POST /support/webhook/email/inbound?adapter=<postmark\|mailgun\|ses>` |
| Spring Boot | `POST /escalated/webhook/email/inbound?adapter=<postmark\|mailgun\|ses>` |
| Go | `POST /escalated/webhook/email/inbound?adapter=<postmark\|mailgun\|ses>` |
| Phoenix | `POST /support/webhook/email/inbound?adapter=<postmark\|mailgun\|ses>` |
| Symfony | `POST /escalated/webhook/email/inbound?adapter=<postmark\|mailgun\|ses>` |

The legacy host-app integrations (Laravel, Rails, Django, Adonis, Filament, WordPress) expose provider-specific endpoints — see the respective framework page for the exact URL.

Expand Down
13 changes: 12 additions & 1 deletion sections/inbound-email/dotnet.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Webhook endpoint

The .NET bundle exposes a single webhook for all providers. Configure Postmark and/or Mailgun to POST inbound mail to:
The .NET bundle exposes a single webhook for all providers. Configure Postmark, Mailgun, or AWS SES (via SNS) to POST inbound mail to:

```
POST /support/webhook/email/inbound?adapter=postmark
Expand Down Expand Up @@ -52,6 +52,17 @@ https://yourapp.com/support/webhook/email/inbound?adapter=mailgun

Set the HMAC header the same way.

**AWS SES** — create an SES receipt rule that publishes to an SNS topic, then subscribe your webhook URL to that topic:

```
https://yourapp.com/support/webhook/email/inbound?adapter=ses
```

SES-specific notes:
- **Subscription confirmation** — AWS SNS sends a one-time `SubscriptionConfirmation` envelope when you first subscribe the endpoint. The bundle throws `SESSubscriptionConfirmationException` carrying the `SubscribeUrl`; your host controller should catch it and GET that URL to activate the subscription (then return `200 OK`).
- **Custom headers** — SNS doesn't forward per-request custom headers from you, but it signs each delivery itself. Since the endpoint is secret-key-guarded, configure your infrastructure (load balancer, API gateway, or CloudFront) to inject the `X-Escalated-Inbound-Secret` header on requests to the SES path.
- **Body extraction** — configure the SES receipt rule with action type `SNS` and encoding `BASE64` to receive the full raw MIME body. The bundle decodes `text/plain`, `text/html`, and `multipart/alternative` bodies. Without full content, metadata (from/to/subject/threading headers) is still extracted and tickets still route correctly via Message-ID threading.

### Testing

```bash
Expand Down
13 changes: 12 additions & 1 deletion sections/inbound-email/go.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Webhook endpoint

The Go module exposes a single webhook handler for all providers. Configure Postmark and/or Mailgun to POST inbound mail to:
The Go module exposes a single webhook handler for all providers. Configure Postmark, Mailgun, or AWS SES (via SNS) to POST inbound mail to:

```
POST /escalated/webhook/email/inbound?adapter=postmark
Expand Down Expand Up @@ -57,6 +57,17 @@ https://yourapp.com/escalated/webhook/email/inbound?adapter=mailgun

Set the HMAC header the same way.

**AWS SES** — create an SES receipt rule that publishes to an SNS topic, then subscribe your webhook URL to that topic:

```
https://yourapp.com/escalated/webhook/email/inbound?adapter=ses
```

SES-specific notes:
- **Subscription confirmation** — AWS SNS sends a one-time `SubscriptionConfirmation` envelope when you first subscribe the endpoint. The parser returns a sentinel `email.ErrSESSubscriptionConfirmation` wrapping an `*email.SESSubscriptionConfirmation` that carries the `SubscribeURL`. Use `errors.As` to unwrap it; your handler should GET that URL to activate the subscription (then return 200).
- **Custom headers** — SNS doesn't forward per-request custom headers, but it signs each delivery itself. Since the endpoint is secret-key-guarded, configure your infrastructure (load balancer, API gateway, or CDN) to inject the `X-Escalated-Inbound-Secret` header on requests to the SES path.
- **Body extraction** — configure the SES receipt rule with action type `SNS` and encoding `BASE64` to receive the full raw MIME body. The module decodes `text/plain`, `text/html`, and `multipart/alternative` bodies using stdlib `net/mail` + `mime/multipart`. Without full content, threading metadata is still extracted and tickets still route correctly via Message-ID threading.

### Testing

```bash
Expand Down
24 changes: 23 additions & 1 deletion sections/inbound-email/phoenix.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Webhook endpoint

The Phoenix library exposes a single webhook controller for all providers. Configure Postmark and/or Mailgun to POST inbound mail to:
The Phoenix library exposes a single webhook controller for all providers. Configure Postmark, Mailgun, or AWS SES (via SNS) to POST inbound mail to:

```
POST /support/webhook/email/inbound?adapter=postmark
Expand Down Expand Up @@ -56,6 +56,28 @@ https://yourapp.com/support/webhook/email/inbound?adapter=mailgun

Set the HMAC header the same way.

**AWS SES** — create an SES receipt rule that publishes to an SNS topic, then subscribe your webhook URL to that topic:

```
https://yourapp.com/support/webhook/email/inbound?adapter=ses
```

SES-specific notes:
- **Subscription confirmation** — AWS SNS sends a one-time `SubscriptionConfirmation` envelope when you first subscribe the endpoint. The parser returns `{:error, {:ses_subscription_confirmation, %{subscribe_url: ..., topic_arn: ..., token: ...}}}`. Match on the tuple in your controller and GET the `subscribe_url` to activate the subscription (then return 200).
- **Custom headers** — SNS doesn't forward per-request custom headers, but it signs each delivery itself. Since the endpoint is secret-key-guarded, configure your infrastructure (load balancer, API gateway, or edge proxy) to inject the `x-escalated-inbound-secret` header on requests to the SES path.
- **Body extraction** — configure the SES receipt rule with action type `SNS` and encoding `BASE64` to receive the full raw MIME body. The library's hand-rolled splitter decodes `text/plain`, `text/html`, `multipart/alternative`, and `quoted-printable` transfer encoding — no external MIME dep. Without full content, threading metadata is still extracted and tickets still route correctly via Message-ID threading.

Register the SES parser alongside the others in `config/runtime.exs`:

```elixir
config :escalated,
inbound_parsers: [
Escalated.Services.Email.Inbound.PostmarkParser,
Escalated.Services.Email.Inbound.MailgunParser,
Escalated.Services.Email.Inbound.SESParser
]
```

### Testing

```bash
Expand Down
13 changes: 12 additions & 1 deletion sections/inbound-email/spring.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Webhook endpoint

The Spring Boot starter exposes a single webhook for all providers. Configure Postmark and/or Mailgun to POST inbound mail to:
The Spring Boot starter exposes a single webhook for all providers. Configure Postmark, Mailgun, or AWS SES (via SNS) to POST inbound mail to:

```
POST /escalated/webhook/email/inbound?adapter=postmark
Expand Down Expand Up @@ -49,6 +49,17 @@ https://yourapp.com/escalated/webhook/email/inbound?adapter=mailgun

Set the HMAC header the same way.

**AWS SES** — create an SES receipt rule that publishes to an SNS topic, then subscribe your webhook URL to that topic:

```
https://yourapp.com/escalated/webhook/email/inbound?adapter=ses
```

SES-specific notes:
- **Subscription confirmation** — AWS SNS sends a one-time `SubscriptionConfirmation` envelope when you first subscribe the endpoint. The starter throws `SESSubscriptionConfirmationException` carrying `getSubscribeUrl()`; your controller should catch it and GET that URL to activate the subscription (then return 200).
- **Custom headers** — SNS doesn't forward per-request custom headers, but it signs each delivery itself. Since the endpoint is secret-key-guarded, configure your infrastructure (load balancer, API gateway, or ingress) to inject the `X-Escalated-Inbound-Secret` header on requests to the SES path.
- **Body extraction** — configure the SES receipt rule with action type `SNS` and encoding `BASE64` to receive the full raw MIME body. The starter decodes `text/plain`, `text/html`, and `multipart/alternative` bodies via `jakarta.mail`. Without full content, threading metadata is still extracted and tickets still route correctly via Message-ID threading.

### Testing

```bash
Expand Down
13 changes: 12 additions & 1 deletion sections/inbound-email/symfony.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Webhook endpoint

The Symfony bundle exposes a single webhook controller for all providers. Configure Postmark and/or Mailgun to POST inbound mail to:
The Symfony bundle exposes a single webhook controller for all providers. Configure Postmark, Mailgun, or AWS SES (via SNS) to POST inbound mail to:

```
POST /escalated/webhook/email/inbound?adapter=postmark
Expand Down Expand Up @@ -48,6 +48,17 @@ https://yourapp.com/escalated/webhook/email/inbound?adapter=mailgun

Set the HMAC header the same way.

**AWS SES** — create an SES receipt rule that publishes to an SNS topic, then subscribe your webhook URL to that topic:

```
https://yourapp.com/escalated/webhook/email/inbound?adapter=ses
```

SES-specific notes:
- **Subscription confirmation** — AWS SNS sends a one-time `SubscriptionConfirmation` envelope when you first subscribe the endpoint. The bundle throws `SESSubscriptionConfirmationException` carrying `$subscribeUrl`; your controller should catch it and GET that URL to activate the subscription (then return 202).
- **Custom headers** — SNS doesn't forward per-request custom headers, but it signs each delivery itself. Since the endpoint is secret-key-guarded, configure your infrastructure (load balancer, API gateway, or edge proxy) to inject the `X-Escalated-Inbound-Secret` header on requests to the SES path.
- **Body extraction** — configure the SES receipt rule with action type `SNS` and encoding `BASE64` to receive the full raw MIME body. The bundle's hand-rolled splitter decodes `text/plain`, `text/html`, `multipart/alternative`, and `quoted-printable` transfer encoding — no external MIME dep. Without full content, threading metadata is still extracted and tickets still route correctly via Message-ID threading.

### Testing

```bash
Expand Down