# Ha Tang Email

Ha Tang Email sends transactional and marketing email from your own domain. It speaks the same API as Resend, so existing Resend code works after you change the base URL and the API key, and it delivers through Amazon SES. This page shows how the pieces fit together and walks you through your first email.

## What you get

| Feature | What it does | Guide |
|---|---|---|
| Sending domains | DKIM, SPF and return path for each domain, one-click DNS setup at supported DNS hosts | [Domains](https://hatang.dev/docs/guides/email/domains.md) |
| REST API | `POST /emails` and `POST /emails/batch`, scheduling, idempotency, tags, attachments | [Send email](https://hatang.dev/docs/guides/email/sending.md) |
| SMTP relay | `smtp.hatang.dev:5870` with STARTTLS, for frameworks and apps that only speak SMTP | [SMTP relay](https://hatang.dev/docs/guides/email/smtp.md) |
| Templates | Reusable emails with variables, sent by id or alias | [Templates](https://hatang.dev/docs/guides/email/templates.md) |
| Tracking | Open and click tracking, optionally on your own subdomain | [Open and click tracking](https://hatang.dev/docs/guides/email/tracking.md) |
| Receiving | Mail sent to your domain becomes an `email.received` webhook | [Receive email](https://hatang.dev/docs/guides/email/receiving.md) |
| Audiences | Contacts, segments, topics and custom properties | [Contacts, segments and topics](https://hatang.dev/docs/guides/email/audiences.md) |
| Broadcasts | Marketing email to a segment, now or on a schedule, with a hosted preferences page | [Broadcasts](https://hatang.dev/docs/guides/email/broadcasts.md) |
| Automations | Per-contact sequences started by events from your app | [Automations](https://hatang.dev/docs/guides/email/automations.md) |
| Suppressions | Hard bounces, complaints and unsubscribes stop further sends | [Suppressions and bounces](https://hatang.dev/docs/guides/email/suppressions-and-bounces.md) |
| Metrics and logs | Delivery, open, click, bounce and complaint rates, plus a log of every API call | [Metrics and logs](https://hatang.dev/docs/guides/email/metrics-and-logs.md) |

## How a message travels

```mermaid
flowchart LR
  A[Your app] -->|REST API or SMTP| B[Ha Tang]
  B -->|checks: verified domain, suppressions, daily quota| C[Send queue]
  C -->|DKIM-signed with your domain| D[Amazon SES]
  D --> E[Recipient's mailbox]
  D -->|delivery, bounce, complaint| B
  B -->|webhooks| A
```

1. Your app calls `POST /emails` or hands a message to the SMTP relay. Both go through the same checks.
2. Ha Tang checks that the `From` domain is verified and belongs to your business, skips suppressed recipients and reserves one email from your daily quota. It answers with the email `id` right away.
3. A worker signs the message with your domain's DKIM key, sets the bounce address on your return-path subdomain (`send.` by default) and hands it to Amazon SES.
4. Amazon SES reports deliveries, bounces and complaints back to Ha Tang. Ha Tang updates the email's status, adds hard bounces and complaints to your suppression list and sends you webhooks.

## Before you start

- A Ha Tang business with the **Email** module turned on. See [Businesses and modules](https://hatang.dev/help/getting-started/modules).
- A domain you can add DNS records to.
- An API key. See [Authentication](https://hatang.dev/docs/guides/getting-started/authentication.md).

## Send your first email

### 1. Add and verify a domain

In the dashboard, open **Email → Domains**, add your domain (for example `example.com` or a subdomain such as `updates.example.com`), publish the DNS records it shows and click **Verify DNS records**. The domain must reach the **Verified** status before you can send from it. The details are in [Domains](https://hatang.dev/docs/guides/email/domains.md).

### 2. Create an API key

Create a key with the `email:send` scope. Keep it on your server. Never put it in a browser or a mobile app.

### 3. Send

**cURL**

```bash
curl -X POST https://api.hatang.dev/emails \
  -H "Authorization: Bearer $HATANG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@example.com>",
    "to": ["customer@example.net"],
    "subject": "Your order is confirmed",
    "html": "<p>Thanks for your order!</p>"
  }'
```

**Node.js**

```ts
import { Hatang } from "@hatang/sdk";

const hatang = new Hatang(process.env.HATANG_API_KEY, { baseUrl: "https://api.hatang.dev" });

const { data, error } = await hatang.emails.send({
  from: "Acme <hello@example.com>",
  to: ["customer@example.net"],
  subject: "Your order is confirmed",
  html: "<p>Thanks for your order!</p>",
});
if (error) throw new Error(`${error.name}: ${error.message}`);
console.log(data.id);
```

**Python**

```python
from hatang import Hatang  # pip install hatang

hatang = Hatang()  # reads HATANG_API_KEY
email = hatang.emails.send({
    "from": "Acme <hello@example.com>",
    "to": ["customer@example.net"],
    "subject": "Your order is confirmed",
    "html": "<p>Thanks for your order!</p>",
})
print(email["id"])
```

**PHP**

```php
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.hatang.dev']);

$res = $client->post('/emails', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
    'json' => [
        'from' => 'Acme <hello@example.com>',
        'to' => ['customer@example.net'],
        'subject' => 'Your order is confirmed',
        'html' => '<p>Thanks for your order!</p>',
    ],
]);
echo json_decode($res->getBody(), true)['id'];
```

The response holds the email's id:

```json
{ "id": "0199e6c1-7a2b-7f00-9c1e-3f6a2b1d9e10" }
```

> **Note**
>
> The Node.js SDK returns `{ data, error }` and never throws. Always pass `baseUrl: "https://api.hatang.dev"` (or set `HATANG_BASE_URL`), because the SDK's built-in default points at a local development server.

### 4. Follow the email

- In the dashboard, **Email → Emails** lists every email with its status, recipients and events.
- `GET /emails/{id}` returns the email and its `status`; `GET /emails/{id}/events` returns each event and each recipient's state.
- For real-time updates, subscribe a webhook to `email.delivered`, `email.bounced` and the other events. See [Webhooks](https://hatang.dev/docs/guides/getting-started/webhooks.md).

## Dashboard map

| Sidebar item | What you do there |
|---|---|
| **Emails** | Every email sent through the API, the SMTP relay or broadcasts. The **Receiving** tab lists mail received on your domains. |
| **Broadcasts** | Write, schedule and send marketing email to a segment. |
| **Templates** | Edit, test and publish templates. |
| **Audiences** | Contacts, segments, topics, properties and the suppression list. |
| **Automations** | Event-driven sequences and their runs. |
| **Metrics** | Volume and rates per day. |
| **Domains** | Sending and receiving domains, DNS records, tracking. |
| **Logs** | Every API call made with your keys. |
| **API Keys** | Create and revoke API keys. |
| **Webhooks** | Endpoints that receive email events. |
| **Email settings** | SMTP connection details, daily quota, the emergency pause and the unsubscribe page. |

## API key scopes

Grant each key only the scopes the system using it needs:

| Endpoints | Required scope |
|---|---|
| `POST /emails`, `POST /emails/batch`, the SMTP relay | `email:send` |
| `PATCH /emails/{id}`, `POST /emails/{id}/cancel` | `email:send` |
| `GET /emails`, `GET /emails/{id}`, `GET /emails/{id}/events` | `email:read` (or `email:send` / `email:write`, which include it) |
| Domains, templates, contacts, segments, topics, broadcasts, events, automations | `email:write` |
| `GET /emails/metrics` | `email:read` (or `email:send` / `email:write`, which include it) |
| `GET /emails/receiving/*` | `email:read` or `email:write`, granted directly. `email:send` alone cannot read received mail. |
| `GET /logs`, `GET /usage` | Any write scope |

A key can also be limited to one sending domain. That key gets `401 restricted_api_key` when it sends from another domain.

## Limits at a glance

| Limit | Value |
|---|---|
| REST API rate | 10 requests per second per business, shared by the email endpoints |
| Recipients per email | 50 across `to`, `cc` and `bcc` |
| Emails per batch call | 100 |
| Email size | 40 MB, body and attachments together |
| Scheduling | Up to 30 days ahead |
| Daily quota | Set per business, resets at midnight UTC. See **Email settings** or `GET /usage`. |

The full list is in [Limits](https://hatang.dev/docs/reference/limits.md) and every error name is in [Errors](https://hatang.dev/docs/reference/errors.md).

## Next steps

- [Domains](https://hatang.dev/docs/guides/email/domains.md): set up DKIM, SPF and DMARC.
- [Send email](https://hatang.dev/docs/guides/email/sending.md): every field of `POST /emails`.
- [SMTP relay](https://hatang.dev/docs/guides/email/smtp.md): connect WordPress, Laravel, Django and others.
- [Migrate from Resend](https://hatang.dev/docs/guides/email/migrate-from-resend.md): what changes when you switch.

Source: https://hatang.dev/docs/guides/email/overview
