# Send emails with Keila

> Add PostShiba as an SMTP sender in Keila.

## What you'll build

A Keila sender that delivers your campaigns through PostShiba over SMTP. Keila keeps your contacts, forms, and unsubscribe links. PostShiba delivers the mail and suppresses addresses that hard bounce or report spam. This works on self-hosted Keila and on Keila Cloud.

## Before you start

- A **Marketing** cluster. Newsletters belong on a bulk cluster, not a transactional one, and the type is locked after create. See [Clusters](/docs/dashboard/clusters/introduction).
- An SMTP username and password. Open your cluster in the dashboard, go to its SMTP credentials, and choose **Create SMTP credential**. Copy the password right away, because it is shown once. You can also use the [SMTP credentials API](/docs/api-reference/smtp-credentials).
- A verified sending domain. See [Add and verify a domain](/docs/add-a-domain). The sender's **From address** must be on it.
- A cluster that is ready to send (`sending_ready` is `true`).

## Install

Nothing to install. Keila has SMTP support built in.

## Pick a plan for your largest list

PostShiba caps each cluster per hour and per rolling 24 hours. The 24-hour cap must cover your largest list plus anything else the cluster sends that day. Keila holds messages back once a sender reaches its **Rate limit per hour**, so set it to your hourly cap.

| Plan | Emails per hour | Emails per 24 hours | Keila Rate limit per hour |
| --- | --- | --- | --- |
| Nano | 100 | 600 | Test sends only |
| Micro | 4,000 | 24,000 | 4,000 |
| Small | 40,000 | 240,000 | 40,000 |
| Medium | 100,000 | 600,000 | 100,000 |
| Large | 200,000 | 1,200,000 | 200,000 |

Keila opens a new SMTP connection for every message, and PostShiba allows 100 new sessions per username in 10 seconds. Set **Rate limit per second** to `8` on every plan to stay under that.

When a cap is full, SMTP answers `451 4.7.0 hourly send limit reached` or `451 4.7.0 daily send limit reached` and does not keep the message. Select **Resize** on the cluster page before a campaign that would go over. See [What happens at your cap](/docs/dashboard/clusters/what-happens-at-your-cap).

## Send your first email

1. In Keila, open **Senders** from the menu and choose **Create**. On Keila Cloud, Keila walks you through a short setup instead: choose **Custom**, then **SMTP**, enter the values from step 3, and open the sender again afterwards for step 4.
2. Fill in **Sender name**, **From address**, and **From name**. The from address must be on your verified domain.
3. Open the **SMTP** tab and enter these values. Choosing **STARTTLS** in **Connection security** sets **Port** to `587`. The username starts with `smtp_`.

```text keila-smtp.txt
From address:         hello@mail.example.com
Server:               smtp.postshiba.com
Authentication:       Password
Username:             smtp_9
Password:             YOUR_PASSWORD
Connection security:  STARTTLS
Port:                 587
```

4. Set **Rate limit per second** to `8` and **Rate limit per hour** to the value for your plan in the table above. Choose **Save**.
5. Create a campaign with the new sender. Open **Send & Schedule** → **Send preview** and enter your own address. The message shows under Events in the PostShiba dashboard.

Every SMTP setting is on [Send emails with SMTP](/docs/send-with-smtp).

## System emails on self-hosted Keila

Self-hosted Keila sends sign-up and password reset emails through a separate mailer set with environment variables. These are app notifications, so use a username and password from a **Transactional** cluster, not the Marketing one. `MAILER_SMTP_FROM_EMAIL` must be on a domain verified on that cluster's tenant.

```bash .env
MAILER_SMTP_HOST=smtp.postshiba.com
MAILER_SMTP_PORT=587
MAILER_SMTP_TLS_MODE=starttls
MAILER_SMTP_AUTH_METHOD=password
MAILER_SMTP_USER=smtp_9
MAILER_SMTP_PASSWORD=YOUR_PASSWORD
MAILER_SMTP_FROM_EMAIL=hello@mail.example.com
```

Keila reads them at startup, so restart Keila after you change them.

## Bounces, complaints, and unsubscribes

Bounce emails go to PostShiba, not to Keila. With an SMTP sender, Keila does not learn about bounces or complaints, so PostShiba stops the mail instead.

| Event | What happens |
| --- | --- |
| Hard bounce | PostShiba suppresses the address. Later sends to it are refused with `550 suppressed`. |
| Spam report | PostShiba suppresses the address the same way. |
| Soft bounce | PostShiba retries delivery. Nothing is suppressed. |
| Unsubscribe | Keila adds its own `List-Unsubscribe` header to campaign emails, plus `List-Unsubscribe-Post` when Keila runs on HTTPS, so PostShiba leaves them alone. Keila marks the contact as unsubscribed. |

Suppressed addresses still look active in Keila. PostShiba refuses them, so they never reach a mailbox, but they stay in your contact counts. Mark them unreachable in Keila now and then to keep your lists clean. Keila only sends campaigns to active contacts.

## Copy suppressions into Keila

Keila's CSV import skips any row whose status is not active, so it cannot mark contacts unreachable. Use the Keila API instead.

1. Export every suppressed address to `postshiba-suppressions.csv`. Follow [How do I export my suppression list?](/docs/knowledge-base/how-do-i-export-my-suppression-list). It takes a read-only Support key and one script.
2. In Keila, open **API Keys** from the menu, create a key, and copy it. It is shown once.
3. Run this script. It sets each address that exists in Keila to `unreachable`. Addresses Keila does not have return `404` and are skipped.

```bash keila-unreachable.sh
export KEILA_URL="https://app.keila.io"
export KEILA_API_KEY="your-keila-api-key"

while read -r email; do
  id=$(jq -rn --arg e "$email" '$e|@uri')
  code=$(curl -sS -o /dev/null -w "%{http_code}" -X PATCH \
    --url "$KEILA_URL/api/v1/contacts/$id?id_type=email" \
    --header "Authorization: Bearer $KEILA_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{"data":{"status":"unreachable"}}')
  echo "$code $email"
done < postshiba-suppressions.csv
```

On self-hosted Keila, set `KEILA_URL` to your own Keila address. Run the script again after each large campaign. Marking an address that is already unreachable does no harm.

## Troubleshooting

| What you see | What to do |
| --- | --- |
| Authentication fails | Check the username and password. Set **Authentication** to `Password` and **Connection security** to `STARTTLS` on port `587`. Port 465 with `SSL/TLS` is not offered. PostShiba accepts `AUTH PLAIN` only. |
| The preview never shows under Events | The campaign uses another sender. Open the campaign settings and pick the PostShiba sender. |
| `550 unverified domain` or `550 from header not aligned with verified domain` | Set the sender's **From address** to an address on your verified domain. |
| `550 kyc_required` | Your team is pending production approval. Until then, send only to team members, live inbox addresses, and inbox forward-to addresses. See [Does PostShiba require production approval?](/docs/knowledge-base/does-capsule-require-production-approval). |
| `550 suppressed` | The contact hard bounced or reported spam. Copy suppressions into Keila as shown above. |
| `451 4.7.0 hourly send limit reached` or `451 4.7.0 daily send limit reached` | The cluster is at its cap and did not keep the message. Lower the sender's **Rate limit per hour**, or resize the cluster before the next campaign. |
| `451 4.4.5 too many sessions, slow down` | Keila opened too many SMTP connections on one username in 10 seconds. Lower the sender's **Rate limit per second**. |

## Next steps

- [What happens at your cap](/docs/dashboard/clusters/what-happens-at-your-cap)
- [How to manage suppressions](/docs/dashboard/emails/email-suppressions)
- [How do recipients unsubscribe?](/docs/knowledge-base/how-do-recipients-unsubscribe)
- [Send emails with SMTP](/docs/send-with-smtp) for every setting and reply.
