# Automations

Automation là chuỗi bước chạy cho một contact khi có chuyện xảy ra trong ứng dụng của bạn: gửi thư chào mừng khi khách đăng ký, cảm ơn sau khi thanh toán, nhắc lại nếu ba ngày sau khách vẫn chưa để lại đánh giá. Ứng dụng của bạn gửi sự kiện, Hạ Tầng chạy các bước. API giống Resend Automations.

## Trước khi bắt đầu

- Các [template](https://hatang.dev/vi/docs/guides/email/templates.md) đã publish cho những email mà automation gửi, với địa chỉ `From` trên tên miền đã xác minh.
- Nếu dùng API, key có `email:write`.

## 1. Khai báo sự kiện

Automation bắt đầu từ một **sự kiện**. Khai báo sự kiện ở **Email → Automations → Sự kiện** hoặc bằng API. Schema không bắt buộc; nếu có, Hạ Tầng kiểm tra và chuyển kiểu các trường của payload (`string`, `number`, `boolean`, `date`):

**cURL**

```bash
curl -X POST https://api.hatang.dev/events \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "order.paid", "schema": { "amount": "number", "order_id": "string" } }'
```

**Node.js**

```ts
await hatang.events.create({ name: "order.paid", schema: { amount: "number", order_id: "string" } });
```

**Python**

```python
requests.post(
    "https://api.hatang.dev/events",
    headers={"Authorization": f"Bearer {os.environ['HATANG_API_KEY']}"},
    json={"name": "order.paid", "schema": {"amount": "number", "order_id": "string"}},
)
```

**PHP**

```php
$client->post('/events', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
    'json' => ['name' => 'order.paid', 'schema' => ['amount' => 'number', 'order_id' => 'string']],
]);
```

- Tên sự kiện gồm chữ, số và `.`, `_`, `:`, `-`, tối đa 100 ký tự.
- Tên bắt đầu bằng `hatang:` hoặc `resend:` được dành cho sự kiện có sẵn. Hiện có một sự kiện: `hatang:contact.created`, phát ra khi một contact được tạo qua API, dashboard hoặc nhập CSV.

## 2. Gửi sự kiện từ ứng dụng

Gửi sự kiện cho một contact theo `contact_id`, hoặc theo `email`. Với `email`, Hạ Tầng tạo contact nếu chưa có.

**cURL**

```bash
curl -X POST https://api.hatang.dev/events/send \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{ "event": "order.paid", "email": "lan@example.vn", "payload": { "amount": 2000000, "order_id": "DH1024" } }'
```

**Node.js**

```ts
const { data } = await hatang.events.send({
  event: "order.paid",
  email: "lan@example.vn",
  payload: { amount: 2000000, order_id: "DH1024" },
});
console.log(data.runs_started, data.runs_resumed);
```

**Python**

```python
hatang.events.send("order.paid", email="lan@example.vn", payload={"amount": 2000000, "order_id": "DH1024"})
```

**PHP**

```php
$client->post('/events/send', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
    'json' => ['event' => 'order.paid', 'email' => 'lan@example.vn', 'payload' => ['amount' => 2000000, 'order_id' => 'DH1024']],
]);
```

Kết quả trả về `202`:

```json
{ "object": "event", "event": "order.paid", "contact_id": "con_0199e6c17a2b7f009c1e3f6a2b1d9e10", "runs_started": 1, "runs_resumed": 0 }
```

`runs_started` là số automation đang bật mà sự kiện này khởi động cho contact; `runs_resumed` là số lượt chạy đang chờ sự kiện này và được chạy tiếp.

| Lỗi | Nguyên nhân |
|---|---|
| `422` "Unknown event" | Sự kiện chưa được khai báo. Tạo bằng `POST /events` trước. |
| `422` "must be a number" (hoặc boolean, date) | Một trường của payload không khớp schema. Trường không có trong schema được giữ nguyên. |
| `422` "Pass contact_id or email" | Request không chỉ định contact nào. |

## 3. Dựng luồng

Mở **Email → Automations**, tạo automation rồi:

1. Chọn sự kiện khởi động (**Bắt đầu từ một sự kiện**).
2. Bấm **+** dưới một bước để thêm bước tiếp theo.
3. Cấu hình bước đang chọn ở khung bên phải.
4. **Lưu**, rồi **Bật**.

| Bước | Cấu hình | Nhánh ra |
|---|---|---|
| `trigger` | `event_name` | |
| `send_email` | `template.id` (template **đã publish**), `from` và `subject` tuỳ chọn để ghi đè template, `template.variables` | |
| `delay` | `duration`, ví dụ `"30 minutes"`, `"2 days"`, `"1 week"`. Tối đa 30 ngày. | |
| `wait_for_event` | `event_name`, `timeout` (mặc định `"30 days"`), `filter_rule` tuỳ chọn trên sự kiện tới | `event_received`, `timeout` |
| `condition` | `rule` | `condition_met`, `condition_not_met` |
| `update_contact` | `properties` (thuộc tính đã khai báo), `unsubscribed` | |
| `add_to_segment` | `segment_id` | |

Khi bước `send_email` chạy:

- Biến của template lấy lần lượt từ `template.variables` của bước, rồi payload của sự kiện, rồi thông tin contact (`contact.first_name`, `contact.<thuộc tính>`…).
- `{{{UNSUBSCRIBE_URL}}}` trỏ tới trang tuỳ chọn của contact.
- Contact đã huỷ đăng ký mọi email thì được bỏ qua (bước được đánh dấu `skipped`).
- Mỗi email có tag `automation_id` và hiện ở **Email → Email** như mọi email khác.

Hạ Tầng không cho bật automation thiếu trigger, dùng template chưa publish, hoặc có bước `send_email` không có địa chỉ `From` ở cả bước lẫn template.

### Điều kiện

Một `rule` so sánh một trường với một giá trị:

```json
{ "field": "event.amount", "operator": "gte", "value": 1000000 }
```

- **Trường**:
  - `event.*`: payload của sự kiện khởi động lượt chạy.
  - `contact.*`: `email`, `first_name`, `last_name`, `unsubscribed` và các thuộc tính tuỳ chỉnh.
  - `wait_events.<tên sự kiện>.*`: payload mà bước `wait_for_event` đã nhận.
- **Toán tử**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `starts_with`, `ends_with`, `exists`, `is_empty`.
- **So sánh với trường khác**: `"value": { "var": "contact.limit" }`.
- **Ghép nhiều điều kiện**: `{ "type": "and", "rules": [ … ] }` hoặc `{ "type": "or", "rules": [ … ] }`.

`filter_rule` của `wait_for_event` có cùng cấu trúc, trong đó `event.*` là sự kiện vừa tới. Lượt chạy đang chờ chỉ tiếp tục khi sự kiện qua được bộ lọc.

### Tạo bằng API

```bash
curl -X POST https://api.hatang.dev/automations \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{
  "name": "Sau khi thanh toán",
  "status": "enabled",
  "steps": [
    { "key": "start", "type": "trigger", "config": { "event_name": "order.paid" } },
    { "key": "thanks", "type": "send_email", "config": { "template": { "id": "order-confirmed" } } },
    { "key": "big", "type": "condition", "config": { "rule": { "field": "event.amount", "operator": "gte", "value": 1000000 } } },
    { "key": "vip", "type": "add_to_segment", "config": { "segment_id": "aud_0199e6c17a2b7f009c1e3f6a2b1d9e10" } },
    { "key": "review", "type": "wait_for_event", "config": { "event_name": "review.left", "timeout": "3 days" } },
    { "key": "remind", "type": "send_email", "config": { "template": { "id": "review-reminder" } } }
  ],
  "connections": [
    { "from": "start", "to": "thanks" },
    { "from": "thanks", "to": "big" },
    { "from": "big", "to": "vip", "type": "condition_met" },
    { "from": "big", "to": "review", "type": "condition_not_met" },
    { "from": "review", "to": "remind", "type": "timeout" }
  ]
}'
# { "object": "automation", "id": "auto_…" }
```

- Mỗi automation có đúng một bước `trigger`, tối đa 100 bước và 200 kết nối.
- Khoá của bước không trùng nhau, gồm chữ, số, `-` và `_`, tối đa 64 ký tự.
- Không bước nào được dẫn ngược về trigger.

## Bật, sửa, dừng

- Automation **đang bật** thì không sửa được các bước. Tắt đi để sửa, hoặc **Nhân bản** thành automation mới.
- Lượt chạy giữ phiên bản automation lúc nó bắt đầu, kể cả khi bạn sửa automation sau đó.
- `POST /automations/{id}/stop` tắt automation và huỷ mọi lượt chạy chưa xong.
- `POST /automations/{id}/duplicate` tạo một bản sao ở trạng thái tắt.

## Lượt chạy

Mỗi lần một contact đi vào automation, Hạ Tầng tạo một **lượt chạy**. Tab **Lượt chạy** liệt kê theo trạng thái:

| Trạng thái | Ý nghĩa |
|---|---|
| `running` | Đang chạy các bước. |
| `waiting` | Đang chờ hết `delay` hoặc chờ một sự kiện. |
| `completed` | Đã đi hết luồng. |
| `failed` | Một bước bị lỗi; lượt chạy hiện thông báo lỗi. |
| `cancelled` | Bị dừng, ví dụ bởi `POST /automations/{id}/stop`. |

Mở một lượt chạy để xem từng bước: chạy lúc nào, kết quả (ví dụ id của email đã gửi) và lỗi nếu có.

```bash
curl "https://api.hatang.dev/automations/auto_…/runs?status=failed,waiting" -H "Authorization: Bearer $HATANG_API_KEY"
curl "https://api.hatang.dev/automations/auto_…/runs/arun_…" -H "Authorization: Bearer $HATANG_API_KEY"
```

Trang dựng luồng có ô **Gửi sự kiện thử**: nhập địa chỉ email và payload để chạy thử một automation đang bật cho contact đó.

## SDK

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

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

await hatang.events.create({ name: "order.paid", schema: { amount: "number" } });
await hatang.events.send({ event: "order.paid", email: "lan@example.vn", payload: { amount: 2000000 } });
const { data } = await hatang.automations.runs.list("auto_…", { status: "failed" });
await hatang.automations.stop("auto_…");
```

## Tham chiếu API

| Endpoint | Mô tả |
|---|---|
| `POST /events`, `GET /events`, `GET`, `PATCH`, `DELETE /events/{id hoặc tên}` | Khai báo sự kiện. `PATCH` đổi schema. |
| `POST /events/send` | Gửi sự kiện cho một contact. |
| `POST /automations`, `GET /automations`, `GET`, `PATCH`, `DELETE /automations/{id}` | Automation. |
| `POST /automations/{id}/stop`, `POST /automations/{id}/duplicate` | Dừng, nhân bản. |
| `GET /automations/{id}/runs`, `GET /automations/{id}/runs/{run_id}` | Lượt chạy (`status`, `limit`, `after`) và một lượt chạy kèm các bước. |

## Bước tiếp theo

- [Templates](https://hatang.dev/vi/docs/guides/email/templates.md)
- [Contacts, segments và topics](https://hatang.dev/vi/docs/guides/email/audiences.md)
- [Số liệu và logs](https://hatang.dev/vi/docs/guides/email/metrics-and-logs.md)

Source: https://hatang.dev/vi/docs/guides/email/automations
