Đổi client_id và client_secret lấy token Bearer có hiệu lực một giờ, chỉ dùng giữa máy chủ với máy chủ.
Hóa đơn đầu tiên trong 6 bước
- Tạo thông tin xác thực trong bảng điều khiển tại Lập hóa đơn → Cài đặt → Truy cập API, rồi sao chép client_secret ngay — secret chỉ hiển thị một lần.
- Gửi POST /v1/auth/token với grant_type client_credentials, client_id và client_secret.
- Bạn nhận lại access_token, token_type Bearer, expires_in 3600 và danh sách các scope được cấp.
- Đặt token vào header Authorization: Bearer <access_token> trên mọi lệnh gọi khác.
- Kiểm tra những gì bạn thực sự nhận được bằng GET /v1/me: tenant, scope và các nhóm giới hạn tần suất hiện tại.
Mọi endpoint
| Phương thức | Endpoint | Scope | Idempotency-Key | Yêu cầu / phút |
|---|---|---|---|---|
| POST | /auth/token Đổi thông tin xác thực lấy access token | — | — | 120 |
| GET | /me Tenant, scope và giới hạn hiện tại | invoices:read | — | 120 |
API 4invoices là gì
bash
curl -sS -X POST https://api.4invoices.net/v1/auth/token \
-H 'Content-Type: application/json' \
-d '{
"grant_type": "client_credentials",
"client_id": "cli_XXXXXXXXXXXX",
"client_secret": "sec_XXXXXXXXXXXXXXXXXXXX"
}'
# → {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600,
# "scope":"invoices:read invoices:write efactura:submit"}bash
curl -sS https://api.4invoices.net/v1/me \
-H "Authorization: Bearer $ACCESS_TOKEN"
# → tenant, các scope được cấp và các nhóm giới hạn tần suất hiện tạijavascript
// Lưu token vào bộ nhớ đệm; đừng tạo một token cho mỗi yêu cầu.
let cached = { token: "", expiresAt: 0 };
async function getToken() {
if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;
const res = await fetch("https://api.4invoices.net/v1/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "client_credentials",
client_id: process.env.FOURINVOICES_CLIENT_ID,
client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(`token ${res.status}`);
const data = await res.json();
cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}https://api.4invoices.net/v1 · https://api.4invoices.net/v1/openapi.json
Scope
- invoices:read
- invoices:write
- clients:read
- clients:write
- efactura:read
- efactura:submit
Chỉ dùng giữa máy chủ với máy chủ. Không bao giờ gọi API từ JavaScript trên trình duyệt: client_secret sẽ đến tay người truy cập, và CORS chặn mọi origin.
Lỗi
| Trạng thái | Loại | Mã |
|---|---|---|
| 401 | authentication_error | invalid_client |
| 401 | authentication_error | token_expired |
| 401 | authentication_error | invalid_token |
| 403 | permission_error | insufficient_scope |
Lỗi thường gặp
- Endpoint token có giới hạn riêng theo IP. Mỗi tiến trình dùng một token, làm mới trước khi hết hạn — không phải một token cho mỗi yêu cầu.
- client_secret chỉ được lưu dưới dạng băm; nếu mất, hãy tạo thông tin xác thực mới và thu hồi thông tin cũ.
- Hãy cấp thông tin xác thực riêng cho ERP, cửa hàng và các script nội bộ, mỗi cái với scope tối thiểu. Thu hồi một cái không ảnh hưởng đến các cái khác.
- CORS không cho phép origin trình duyệt nào: API được thiết kế để chỉ dùng giữa máy chủ với máy chủ.
Câu hỏi và trả lời cho nhà phát triển
Điều gì xảy ra nếu client_secret sai?
Bạn nhận được 401 với mã invalid_client. Một secret quá ngắn sẽ bị loại sớm hơn, ở bước xác thực, với 422 validation_failed và param client_secret.
Có môi trường sandbox riêng không?
Không. Chỉ có một máy chủ sản xuất. Hãy thử nghiệm bằng hóa đơn proforma (document_kind proforma) và bằng /invoices/preview-totals, vốn không tiêu tốn số hóa đơn tài chính nào.
Tôi có thể thu hẹp scope của thông tin xác thực hiện có không?
Có, từ bảng điều khiển. Các token đã được cấp vẫn giữ những scope mà chúng được tạo ra cho đến khi hết hạn, trong vòng một giờ.