API của Kallo
Kallo có một HTTP API. Nó chính là API mà ứng dụng web và ứng dụng di động đang dùng — không có một API công khai riêng — và trang này mô tả nó một cách thành thật, kể cả những phần chưa sẵn sàng để bạn xây dựng lên trên.
Bản mô tả máy đọc được nằm ở
https://kallo.fit/openapi.json. Đó là OpenAPI
3.1, và mỗi thao tác đều có operationId duy nhất cùng phần mô tả. Với route
kiểm tra thân yêu cầu bằng một schema, thân yêu cầu trong tài liệu được sinh ra
từ chính schema đó. Một bài kiểm thử CI cũng so đặc tả với các route handler, nên
nó sẽ báo lỗi nếu thân yêu cầu, mã trạng thái thành công hay tham số truy vấn
trong tài liệu khác với những gì handler thực sự đọc và trả về.
Địa chỉ gốc
https://kallo.fit
Các endpoint nằm dưới /api/v1/. Ngoại lệ là /api/healthz, không đánh phiên
bản vì nó là kiểm tra sống chứ không phải một phần của bề mặt API, và hai route
cần xác thực: POST /api/analyze-meal và GET /api/og/macro-card/{shareId}
(mô tả bên dưới).
Phản hồi thành công là 200 (riêng liên kết xác nhận danh sách chờ là chuyển
hướng 307). Không endpoint nào trả 201, kể cả những cái tạo ra dữ liệu mới.
Các lệnh đọc chia theo ngày đều cần tham số truy vấn tz bắt buộc: độ lệch tính
bằng phút như Date.getTimezoneOffset() trả về, từ -840 đến 720. Nếu thiếu,
bạn nhận 400; máy chủ không bao giờ tự lấy múi giờ đã lưu. Ngoại lệ duy nhất
là tổng quan dinh dưỡng: ở đó tz là tùy chọn, và khi thiếu thì ngày được chia
theo UTC.
Phân tích bữa ăn và thẻ macro
POST /api/analyze-meal biến một câu mô tả thành bản ước tính. Nó trả về một
luồng text/event-stream gồm các sự kiện server-sent: stage báo tiến độ,
item_name và item_macros khi tìm thấy từng món, rồi result (hoặc
cheat_estimate). Luồng kết thúc bằng analysis_complete, kèm một analysisId,
hoặc bằng error. Chưa có gì được lưu cho đến khi bạn gửi id đó tới
POST /api/v1/meals/confirm. Xác thực, kiểm tra thân yêu cầu, gói dịch vụ và giới
hạn tần suất đều chạy trước khi luồng mở và trả về lỗi JSON thông thường bên dưới.
Khi luồng đã mở, lỗi chỉ có thể đến dưới dạng sự kiện error. Thân phản hồi là
văn bản gồm các frame (event: <type>, rồi data: <json>); component OpenAPI
AnalyzeMealStreamEvent mô tả một payload data.
GET /api/og/macro-card/{shareId} vẽ một bữa ăn được chia sẻ thành thẻ PNG. Nó
cần phiên đăng nhập và áp dụng cùng quy tắc hiển thị như khi đọc bữa ăn được chia
sẻ, nên một lượt chia sẻ bạn không được xem sẽ trả 404, giống hệt một lượt không
tồn tại.
Xác thực
Mọi endpoint trừ những cái gắn nhãn Public đều cần một access token do Supabase
cấp cho một người dùng Kallo:
Authorization: Bearer <jwt>
Token không mang phạm vi (scope) nào. Ai giữ token là giữ toàn bộ quyền của
tài khoản: đọc mọi bữa ăn, đổi hồ sơ, và xoá tài khoản. Không có cách nào cấp một
token hẹp hơn. Đó là lý do
metadata tài nguyên được bảo vệ
không khai báo scopes_supported — công bố một danh sách scope mà không có gì
thực thi còn tệ hơn là không công bố gì.
Metadata này theo RFC 9728 và nêu tên máy chủ uỷ quyền là Supabase Auth. Bản thân
Kallo không phải máy chủ uỷ quyền: không có /oauth/authorize, không có
/oauth/token, và không có đăng ký client động.
Lỗi
Mọi lỗi API đã được tài liệu hóa đều trả về cùng một cấu trúc:
{
"error": {
"code": "VALIDATION_FAILED",
"status": 400,
"retryable": false,
"message": "date must be YYYY-MM-DD.",
"resolution": "Correct the request using the published schema, then retry."
}
}
code là mã ổn định, dùng để rẽ nhánh trong code. message dành cho người đọc
và có thể được dịch. retryable cho biết việc lặp lại cùng yêu cầu có thể thành
công hay không. resolution cung cấp bước tiếp theo cho agent mà không buộc nó
phải phân tích văn bản dành cho con người. Các thao tác chỉ chuyển hướng và health
probe mô tả cấu trúc phản hồi riêng trong OpenAPI.
| Mã | Trạng thái | Thử lại được | Nghĩa là |
|---|---|---|---|
VALIDATION_FAILED | 400 | không | Thân yêu cầu hoặc query sai, kể cả thân yêu cầu không phải JSON hợp lệ. Hãy sửa. |
NOT_AUTHENTICATED | 401 | không | Thiếu token, token hết hạn hoặc không hợp lệ. |
feature_locked | 402 | không | Gói của tài khoản không bao gồm tính năng này. |
NOT_FOUND | 404 | không | Không có tài nguyên đó, hoặc nó không phải của bạn. Hai trường hợp này cố ý không phân biệt được. |
PROFILE_NOT_FOUND | 404 | không | Người dùng đã đăng nhập chưa có hồ sơ. Hãy đăng nhập lại hoặc hoàn tất onboarding. |
BARCODE_NOT_FOUND | 404 | không | Không có sản phẩm nào ứng với mã vạch này. |
BARCODE_NOT_CACHED | 404 | không | Mã vạch chưa từng được tra cứu trước khi ghi. Hãy quét lại. |
OCR_INVALID_IMAGE | 400 | không | Không đọc được ảnh như một ảnh nhãn dinh dưỡng. |
OCR_NO_LABEL_DETECTED | 422 | không | Không tìm thấy bảng dinh dưỡng trong ảnh. |
CONFLICT | 409 | không | Tài nguyên đã thay đổi trong lúc bạn thao tác, hoặc id do client tạo mà bạn gửi (ví dụ mealId / newMealId của bữa ăn) đã được dùng. Hãy tạo id mới. |
CIRCLE_LIMIT_REACHED | 409 | không | Nhóm hoặc danh sách bạn bè của người khác đã đầy. |
PAYLOAD_TOO_LARGE | 413 | không | Thân yêu cầu vượt giới hạn của endpoint (64 KB với phần lớn endpoint JSON). Hãy gửi ít hơn. |
RATE_LIMITED | 429 | có | Hãy giãn nhịp. Tôn trọng Retry-After khi có. |
RATE_LIMITER_UNAVAILABLE | 503 | có | Bộ giới hạn không đưa ra được quyết định trên một route tốn phí. Tôn trọng Retry-After. |
PIPELINE_TIMEOUT | 504 | có | Phân tích bữa ăn quá lâu. |
INTERNAL | 500 | có | Lỗi của chúng tôi. |
Xác thực được kiểm tra trước khi đọc thân yêu cầu. Yêu cầu không có token hợp lệ
nhận NOT_AUTHENTICATED dù thân yêu cầu thế nào, nên 400 hoặc 413 nghĩa là token
đã được chấp nhận. Trùng id do client tạo luôn trả cùng một CONFLICT, dù id đó
là của bạn hay của tài khoản khác; thử lại với cùng id sẽ không bao giờ thành
công. Nếu một lần lưu bị hết thời gian chờ và bạn không chắc nó đã ghi hay chưa,
hãy tải lại ngày đó trước khi thử lại với id mới.
Bất kỳ đường dẫn /api/ nào không có handler đều trả 404 với cùng cấu trúc JSON
này, không bao giờ là trang HTML — nên một endpoint gõ sai là thứ client của bạn
đọc được, chứ không phải thứ nó phải đoán.
Giới hạn tần suất
Không có hạn mức công bố theo từng endpoint. Giới hạn có tồn tại và được áp theo
người dùng, còn với các endpoint không cần xác thực thì theo địa chỉ IP. Khi chạm
giới hạn bạn nhận RATE_LIMITED kèm header Retry-After ở những chỗ đã biết thời
gian chờ. Hãy coi header đó là nguồn đúng, thay vì lùi theo một lịch cố định.
Bạn được dựa vào cái gì
Các thao tác gắn nhãn Public trong đặc tả là an toàn để phụ thuộc:
GET /api/healthzPOST /api/v1/waitlistGET /api/v1/waitlist/confirmGET /api/v1/groups/invite/{slug}GET /api/v1/barcode/image/{code}
Tất cả phần còn lại đánh dấu x-internal: true. Những endpoint đó tồn tại để phục
vụ chính các client của Kallo. Chúng được đánh phiên bản dưới /v1, nhưng phiên
bản ấy không phải lời hứa với bên thứ ba — một cấu trúc có thể đổi để hợp với ứng
dụng mà không tăng phiên bản. Nếu bạn xây dựng lên trên, hãy ràng buộc kỳ vọng
lỏng, và đọc lại đặc tả trước khi nâng cấp.
Chi phí
Gói miễn phí là thật, không cần thẻ, và đủ để dùng API cho mọi việc bạn làm được trong ứng dụng với tài khoản miễn phí. Không có giá riêng cho API và không có môi trường sandbox — bạn phát triển trên production, với tài khoản của chính bạn và dữ liệu của chính bạn.
Liên quan
- Tác nhân AI nên dùng Kallo thế nào
- Đặc tả OpenAPI
- Dữ liệu của bạn — cái gì được lưu, và cái gì tới mô hình
- Liên hệ