# FlutterFlow

FlutterFlow biên dịch thành một ứng dụng mà người dùng của bạn cầm trên tay, vì vậy vấn đề không phải là điền vào những trường nào, mà là khóa sẽ nằm ở đâu. Yêu cầu dưới đây được gửi đến môi trường sản xuất chính xác như cách một lời gọi tạo ra nó; các bước trong FlutterFlow được viết theo tài liệu FlutterFlow tính đến ngày 2026-09-17.

Trước tiên, hãy tạo một khóa, như mô tả trong phần [Khóa tự động hóa](/integrations/#ключ-для-автоматизації), với phạm vi `chart`.

<Aside type="caution" title="Khóa trong lời gọi thông thường sẽ đi kèm với ứng dụng">
Một lời gọi không được đánh dấu là riêng tư sẽ được thực thi từ thiết bị, và tiêu đề của nó nằm bên trong bản dựng đã biên dịch cho iOS, Android và web, nơi bất kỳ ai cũng có thể đọc được. Hãy đặt lời gọi **riêng tư**: khi đó FlutterFlow sẽ thực hiện nó thông qua Firebase Cloud Function, và khóa sẽ nằm trên máy chủ. Kiểm tra riêng của FlutterFlow là tải mã dự án xuống và tìm khóa trong các tệp frontend, nhưng tính năng này chỉ có sẵn trên gói trả phí.
</Aside>

## 1. Tạo lời gọi

Mở **API Calls**, nhấp **+ Add**, chọn **Create API Call** và điền vào:

- **API Call Name**: `Natal chart`.
- **Method Type**: `POST`.
- **URL**:

  ```
  https://api.astroway.info/v1/chart
  ```

- Tab **Headers**, **+ Add Header**: một dòng `X-Api-Key: ` và sau đó là khóa của bạn. Dòng thứ hai `Content-Type: application/json`.
- Tab **Variables**: năm biến kiểu String, `date`, `time`, `timezone`, `latitude`, `longitude`, mỗi biến có giá trị mặc định từ JSON bên dưới.
- Tab **Body**, trong danh sách thả xuống **JSON**: dán JSON và kéo các biến vào đó.

```json
{
  "date": "1990-05-15",
  "time": "14:30:00",
  "timezone": "Europe/Kyiv",
  "latitude": "50.45",
  "longitude": "30.52"
}
```

Vĩ độ và kinh độ được giữ nguyên là chuỗi một cách có chủ đích: API chấp nhận `"50.45"` như một số, nhưng chuỗi giúp việc kéo thả biến dễ dàng hơn.

Nhấp **Add Call**, mở **Response & Test**, nhấp **Test API Call** và xem phản hồi trong **Test Response**. `data.input.timezoneOffset` trả về là `4`: đây chính là độ lệch mà Kyiv đã giữ vào ngày đó.

## 2. Đặt lời gọi riêng tư

Trong **Advanced Settings** của lời gọi, bật **Make Private**, nhấp **Save**, sau đó **Deploy APIs**. Bây giờ lời gọi được thực thi trong Firebase Cloud Function, vì vậy dự án cần có Firebase được kết nối và gói Firebase có Cloud Functions.

<Aside type="caution" title="Lời gọi riêng tư không kế thừa tiêu đề nhóm">
Nếu đặt khóa vào **API Group** thay vì chính lời gọi, FlutterFlow sẽ ngừng gửi nó ngay khi lời gọi trở thành riêng tư: các tiêu đề nhóm không được kế thừa bởi các lời gọi riêng tư. Hãy thêm `X-Api-Key` vào chính lời gọi, nếu không lần chạy riêng tư đầu tiên sẽ trả về `401`.
</Aside>

Hai điều nữa mà tài liệu FlutterFlow nói rõ: khóa được truyền từ frontend, ví dụ qua remote config, vẫn sẽ mở ngay cả trong lời gọi riêng tư, và hình thức an toàn là một tiêu đề được ghi vào chính lời gọi.

## 3. Đọc phản hồi

Trong **JSON Paths**, nhấp **+ Add JSON Path** và đặt tên cho từng cái:

| Tên | JSON Path |
|---|---|
| `ascendant` | `$.data.houses.ascendant` |
| `sunLongitude` | `$.data.planets[0].longitude` |
| `moonLongitude` | `$.data.planets[1].longitude` |
| `offsetUsed` | `$.data.input.timezoneOffset` |

Cung hoàng đạo là kinh độ chia cho 30 và làm tròn xuống, như một chỉ mục trong danh sách mười hai tên: ví dụ, Cung Mọc `159.26` cho ra `5`, tức là Xử Nữ. Một hàm tùy chỉnh bốn dòng hoặc một danh sách mười hai dòng và một chỉ mục là đủ.

## Bản dựng web không thể gọi chúng tôi từ trình duyệt

Đo lường vào 2026-09-17 từ một trang trên miền của bên thứ ba: `POST /v1/chart` phản hồi preflight mà không có tiêu đề `access-control-allow-origin`, và Chrome chặn yêu cầu. Trình duyệt trên miền khác chỉ được phản hồi bởi `/v1/public/*` và `/v1/embed/*`.

FlutterFlow thực hiện các lời gọi thông qua proxy riêng của nó trong trình tạo, ở chế độ Run và Test, vì vậy một lời gọi hoạt động ở đó có thể bị lỗi sau khi ứng dụng web được xuất bản. Có hai giải pháp, và chúng tôi khuyến nghị giải pháp đầu tiên:

- **Đặt lời gọi riêng tư**, như trên. Khi đó nó được thực thi trong Cloud Function, chứ không phải trong trình duyệt, và điều này ngay lập tức giải quyết cả vấn đề khóa và vấn đề CORS.
- **Đặt một backend riêng của bạn trước chúng tôi**, nếu bạn đã có. Cài đặt proxy trong FlutterFlow, cùng với URL proxy riêng, được ghi lại cho trình tạo và các chế độ Run và Test; tài liệu không nói gì về bản dựng web đã xuất bản, vì vậy không nên trông cậy vào chúng ở đó.

Khóa công khai `pk_` không giải quyết được vấn đề này: nó chỉ truy cập được `/v1/public/*` và `/v1/embed/*`, và bị từ chối hoàn toàn từ các bản dựng cho iOS và Android, vì chúng không gửi web-origin.

## Lỗi

- **`401`** trên lời gọi riêng tư: khóa nằm trên nhóm, chứ không phải trên lời gọi.
- **`400 INVALID_FIELD` với `timezone` trong `details`**: múi giờ trống, là một từ viết tắt như `EST` hoặc không phải tên múi giờ.
- **`400` với `date` hoặc `time` trong `details`**: định dạng không phải `YYYY-MM-DD` / `HH:MM:SS`.
- **`403 ENDPOINT_NOT_IN_SCOPE`**: phạm vi khóa không có `chart`.
- **`429 KEY_BUDGET_EXHAUSTED`**: khóa đã hết ngân sách; hãy tăng nó trong bảng điều khiển.

<Aside type="note">
Gói miễn phí của FlutterFlow cho phép hai endpoint API mỗi dự án và không có tính năng nhập OpenAPI, vì vậy trên đó, lời gọi được thêm thủ công, và vẫn còn một slot. API không mã hóa địa lý: tọa độ được lấy từ các trường riêng của bạn hoặc từ dịch vụ mã hóa địa lý mà bạn có khóa.
</Aside>
