API nhà phát triển 4invoices

REST qua HTTPS, JSON, đặc tả OpenAPI công khai. Lập hóa đơn, khách hàng, dãy số và toàn bộ quy trình hóa đơn điện tử quốc gia (ANAF e-Factura tại Romania) — từ ERP, cửa hàng hoặc CRM của bạn.

API công khai của 4invoices đưa dữ liệu hóa đơn từ hệ thống của bạn vào 4invoices và đưa hóa đơn đi hết chặng qua hệ thống hóa đơn điện tử quốc gia (tại Romania: SPV của ANAF). Một máy chủ sản xuất, một luồng client_credentials, các thao tác ghi hóa đơn có tính idempotent và một khung lỗi thống nhất trên mọi tuyến.

https://api.4invoices.net/v1 · Đặc tả OpenAPI (không cần khóa)

API 4invoices là gì

  • API công khai của 4invoices là REST qua HTTPS, nhận JSON và trả về JSON.
  • Một máy chủ sản xuất duy nhất: https://api.4invoices.net/v1. Không có máy chủ sandbox riêng.
  • Đặc tả OpenAPI 3.1 là công khai và không cần khóa — nhập trực tiếp vào Postman hoặc công cụ sinh mã client.
  • Mọi phản hồi đều có header X-Request-Id; hãy cung cấp mã này khi bạn yêu cầu hỗ trợ.
  • Mọi thao tác ghi liên quan đến tài chính đều có tính idempotent: thử lại sau khi hết thời gian chờ sẽ không bao giờ phát hành chứng từ thứ hai.
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.
  • Toàn bộ chu trình e-Factura ANAF (Romania) qua API: kiểm tra sơ bộ, nộp, theo dõi, phản hồi có chữ ký và hộp thư đến. Không đối thủ nào tại Romania cung cấp đầy đủ điều này.
  • SmartBill không có endpoint nào để nộp lên hệ thống hóa đơn điện tử quốc gia (ANAF, tại Romania) — việc gửi chỉ là một cài đặt tài khoản trên giao diện — và hoàn toàn không có endpoint liệt kê hóa đơn.
  • Oblio chỉ có thể tạo khách hàng và sản phẩm như một hệ quả phụ khi phát hành chứng từ, chứ không phải như các tài nguyên hạng nhất.
  • Tạo XML UBL (CIUS-RO cho Romania) mà không cần nộp: bạn nhận được chứng từ pháp lý dưới dạng tệp mà không cần chạm vào hệ thống hóa đơn điện tử quốc gia.
  • Tính idempotent cho thao tác ghi tài chính: thử lại sau khi hết thời gian chờ không tạo ra hóa đơn thứ hai hay tiêu tốn thêm một số.
  • Hộp thư đến hóa đơn nhà cung cấp có thể đọc bằng lập trình — nền tảng cho đối soát tự động.

Hóa đơn đầu tiên trong 6 bước

Trước lệnh gọi đầu tiên
  1. 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 — secret chỉ hiển thị một lần.
  2. Hoàn tất hồ sơ doanh nghiệp và kích hoạt ít nhất một dãy số thuộc loại fiscale, nếu không việc phát hành sẽ trả về issuer_profile_incomplete hoặc no_series_configured.
  1. 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 — secret chỉ hiển thị một lần.
  2. Hoàn tất hồ sơ doanh nghiệp và kích hoạt ít nhất một dãy số thuộc loại fiscale, nếu không việc phát hành sẽ trả về issuer_profile_incomplete hoặc no_series_configured.
  3. Đổi thông tin xác thực lấy token: POST /v1/auth/token với grant_type client_credentials. Token có hiệu lực một giờ — hãy lưu tạm, đừng xin mới cho mỗi yêu cầu.
  4. Phát hành hóa đơn: POST /v1/invoices với header Idempotency-Key và vat_rate dưới dạng phần trăm (21, không phải 0,21).
  5. Chạy kiểm tra sơ bộ (CIUS-RO cho Romania) bằng POST /v1/invoices/{id}/efactura/validate, rồi gửi lên hệ thống hóa đơn điện tử quốc gia bằng POST /v1/invoices/{id}/efactura/submit.
  6. Theo dõi trạng thái bằng GET /v1/efactura/submissions/{id} cho đến khi đạt accepted hoặc rejected.
bash
# 1 — token (1 giờ, lưu vào bộ nhớ đệm)
ACCESS_TOKEN=$(curl -sS -X POST https://api.4invoices.net/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{"grant_type":"client_credentials","client_id":"cli_XXX","client_secret":"sec_XXX"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')

# 2 — phát hành hóa đơn (BẮT BUỘC có Idempotency-Key)
curl -sS -X POST https://api.4invoices.net/v1/invoices \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-10457-invoice' \
  -d '{"buyer":{"name":"Exemplu Comert SRL","tax_id":"RO12345678","county":"Cluj","city":"Cluj-Napoca","country":"RO"},
       "lines":[{"description":"Abonament SaaS","quantity":1,"unit_price":400,"vat_rate":21}]}'

# 3 — preflight CIUS-RO, then send to ANAF
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate \
  -H "Authorization: Bearer $ACCESS_TOKEN"

curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Idempotency-Key: efactura-inv_01J7ABC-1'
javascript
const BASE = "https://api.4invoices.net/v1";

const token = await fetch(`${BASE}/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,
  }),
}).then((r) => r.json()).then((d) => d.access_token);

const invoice = await fetch(`${BASE}/invoices`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    "Idempotency-Key": `order-${orderId}-invoice`,
  },
  body: JSON.stringify({
    buyer: { name: "Exemplu Comert SRL", tax_id: "RO12345678", county: "Cluj", city: "Cluj-Napoca", country: "RO" },
    lines: [{ description: "Abonament SaaS", quantity: 1, unit_price: 400, vat_rate: 21 }],
    currency: "RON",
    metadata: { order_id: String(orderId) },
  }),
}).then((r) => r.json());

console.log(invoice.number, invoice.totals); // "FCT-000123" { net, vat, gross }
python
import os, uuid, requests

BASE = "https://api.4invoices.net/v1"

token = requests.post(f"{BASE}/auth/token", json={
    "grant_type": "client_credentials",
    "client_id": os.environ["FOURINVOICES_CLIENT_ID"],
    "client_secret": os.environ["FOURINVOICES_CLIENT_SECRET"],
}, timeout=30).json()["access_token"]

headers = {
    "Authorization": f"Bearer {token}",
    # Một khóa cho mỗi thao tác LOGIC — dùng lại khi thử lại, không bao giờ tạo mới.
    "Idempotency-Key": f"order-{order_id}-invoice",
}

invoice = requests.post(f"{BASE}/invoices", headers=headers, json={
    "buyer": {"name": "Exemplu Comert SRL", "tax_id": "RO12345678",
              "county": "Cluj", "city": "Cluj-Napoca", "country": "RO"},
    "lines": [{"description": "Abonament SaaS", "quantity": 1,
               "unit_price": 400, "vat_rate": 21}],   # phần trăm, không phải 0,21
    "currency": "RON",
}, timeout=60).json()

print(invoice["number"], invoice["totals"])

Xác thực

  • Luồng xác thực là client_credentials: bạn đổi client_id + client_secret lấy access_token dạng Bearer.
  • Token hết hạn sau 3600 giây. Hãy làm mới trước khi hết hạn, không làm mới ở mỗi lần gọi — POST /auth/token có giới hạn tần suất theo IP riêng.
  • client_secret chỉ được lưu dưới dạng băm và không thể khôi phục. Nếu làm mất, hãy tạo thông tin xác thực mới và thu hồi thông tin cũ.
  • Chỉ dùng giữa máy chủ với máy chủ. Không có origin trình duyệt nào được phép qua CORS: nếu gọi từ JavaScript trên trang web, secret sẽ bị lộ cho người truy cập.
  • Scope được cấp theo từng thông tin xác thực. Hãy tạo thông tin xác thực riêng cho mỗi hệ thống và chỉ cấp cho mỗi hệ thống số scope tối thiểu mà nó cần.

Lập hóa đơn → Cài đặt → Truy cập API

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"}
javascript
// 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;
}

Thuế suất GTGT là phần trăm

vat_rate: 21 ✓  ·  vat_rate: 0.19 ✗  ·  vat_rate: 1 ✗
  • vat_rate là PHẦN TRĂM: 21 nghĩa là 21%.
  • Các giá trị trong khoảng (0, 1] bị từ chối — 0,19 là một phân số, còn 1 không rõ là 1% hay 100%.
  • 0 là hợp lệ và có nghĩa là miễn thuế hoặc đảo ngược nghĩa vụ thuế (reverse charge); hãy ghi căn cứ pháp lý vào notes.
  • unit_price là đơn giá CHƯA THUẾ khi bên phát hành là đối tượng nộp GTGT.
  • Cần tổng tiền trước khi chốt? POST /v1/invoices/preview-totals thực hiện cùng phép tính mà không sử dụng số hóa đơn tài chính.

Tính idempotent của thao tác ghi tài chính

  • Header Idempotency-Key là BẮT BUỘC trên POST /invoices, POST /invoices/{id}/storno và POST /invoices/{id}/efactura/submit.
  • Khóa được ghi nhớ trong 24 giờ. Phát lại cùng khóa với cùng nội dung sẽ trả về kết quả gốc và đặt Idempotency-Replayed: true.
  • Cùng khóa nhưng khác nội dung sẽ trả về 409 idempotency_key_reuse — một chứng từ tài chính đã phát hành không bao giờ bị ghi đè.
  • Một khóa cho mỗi thao tác LOGIC, không phải cho mỗi lần thử HTTP: order-10457-invoice, không phải một UUID mới ở mỗi lần thử lại.
  • Lý do mang tính pháp lý, không phải kỹ thuật: số tài chính đã được sử dụng và hóa đơn đã nộp lên hệ thống hóa đơn điện tử quốc gia (tại Romania: SPV) không thể rút lại.
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-10457-invoice' \
  -d '{
    "buyer": {
      "name": "Exemplu Comert SRL",
      "tax_id": "RO12345678",
      "address": "Str. Fabricii 12",
      "city": "Cluj-Napoca",
      "county": "Cluj",
      "country": "RO",
      "email": "[email protected]"
    },
    "lines": [
      { "description": "Abonament SaaS, iulie", "quantity": 1, "unit_price": 400, "vat_rate": 21, "unit": "buc" },
      { "description": "Implementare", "quantity": 6, "unit_price": 250, "vat_rate": 21, "unit": "ora" }
    ],
    "currency": "RON",
    "document_kind": "factura",
    "due_date": "2026-08-15",
    "metadata": { "order_id": "10457" }
  }'

Mọi endpoint

Phương thứcEndpointScopeIdempotency-KeyYê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
POST/invoices
Phát hành hóa đơn hoặc proforma
invoices:writebắt buộc30
GET/invoices
Liệt kê hóa đơn (phân trang bằng con trỏ)
invoices:read—120
POST/invoices/preview-totals
Tính tổng tiền mà không tạo bất cứ thứ gì
invoices:read—120
GET/invoices/{invoice_id}
Lấy một hóa đơn
invoices:read—120
GET/invoices/{invoice_id}/pdf
Tải PDF hóa đơn
invoices:read—120
GET/invoices/{invoice_id}/xml
Tải XML UBL (CIUS-RO cho Romania)
invoices:read—120
POST/invoices/{invoice_id}/storno
Phát hành hóa đơn điều chỉnh hủy hóa đơn này
invoices:writebắt buộc30
POST/invoices/{invoice_id}/payments
Đánh dấu hóa đơn đã thanh toán
invoices:write—30
DELETE/invoices/{invoice_id}/payments
Đánh dấu hóa đơn chưa thanh toán
invoices:write—30
GET/clients
Liệt kê khách hàng
clients:read—120
POST/clients
Tạo mới hoặc cập nhật khách hàng
clients:write—30
GET/lookup/company/{tax_id}
Xác thực CUI với sổ đăng ký ANAF (Romania)
clients:read—120
GET/series
Liệt kê dãy số
invoices:read—120
POST/invoices/{invoice_id}/efactura/validate
Kiểm tra sơ bộ (CIUS-RO cho Romania) — xác thực mà không gửi
efactura:read—120
POST/invoices/{invoice_id}/efactura/submit
Nộp hóa đơn lên hệ thống hóa đơn điện tử quốc gia (tại Romania: SPV)
efactura:submitbắt buộc10
GET/efactura/submissions
Liệt kê các lần nộp lên hệ thống hóa đơn điện tử quốc gia
efactura:read—120
GET/efactura/submissions/{submission_id}
Lấy một lần nộp
efactura:read—120
POST/efactura/submissions/{submission_id}/refresh
Hỏi hệ thống hóa đơn điện tử quốc gia về trạng thái hiện tại
efactura:read—10
GET/efactura/submissions/{submission_id}/download
Tải kho lưu trữ phản hồi có chữ ký
efactura:read—120
GET/efactura/inbox
Hóa đơn nhà cung cấp nhận qua hệ thống hóa đơn điện tử quốc gia
efactura:read—120
GET/efactura/inbox/{inbound_id}
Lấy một hóa đơn đến
efactura:read—120
GET/efactura/inbox/{inbound_id}/download
Tải kho lưu trữ hóa đơn đến
efactura:read—120
POST/efactura/inbox/sync
Kéo thông điệp mới từ hệ thống hóa đơn điện tử quốc gia vào hộp thư đến
efactura:read—10

25 endpoints · https://api.4invoices.net/v1/openapi.json

Scope

  • invoices:read
  • invoices:write
  • clients:read
  • clients:write
  • efactura:read
  • efactura:submit

Chu trình hóa đơn điện tử quốc gia

not_sentvalidatingqueuedsentprocessingacceptedrejectederror
  • Điểm khác biệt thực sự: toàn bộ chu trình với hệ thống hóa đơn điện tử quốc gia (tại Romania: ANAF e-Factura) chạy qua API, không chỉ dừng ở việc tạo chứng từ.
  • POST /invoices/{id}/efactura/validate chạy kiểm tra sơ bộ (CIUS-RO cho Romania) và trả về đúng các trường mà hệ thống hóa đơn điện tử quốc gia sẽ từ chối, mà không gửi bất cứ thứ gì.
  • GET /invoices/{id}/xml tạo XML UBL (CIUS-RO cho Romania) mà không cần nộp — hữu ích cho kho lưu trữ hoặc kiểm toán nội bộ của bạn.
  • POST /invoices/{id}/efactura/submit nộp hóa đơn lên hệ thống hóa đơn điện tử quốc gia (tại Romania: SPV). Thao tác này không thể hoàn tác: một bản sao thứ hai của cùng hóa đơn không thể rút khỏi hệ thống.
  • Vòng đời được chuẩn hóa: not_sent → validating → queued → sent → processing → accepted | rejected | error. accepted nghĩa là đã truyền hợp lệ về mặt pháp lý; error là lỗi truyền tải phía chúng tôi và an toàn để thử lại.
  • GET /efactura/submissions/{id}/download tải kho lưu trữ kèm phản hồi có chữ ký của hệ thống hóa đơn điện tử quốc gia (tại Romania: ANAF).
  • Chiều nhận hóa đơn cũng có sẵn: POST /efactura/inbox/sync kéo các thông điệp mới từ hệ thống hóa đơn điện tử quốc gia (tại Romania: SPV) về và GET /efactura/inbox liệt kê các hóa đơn mà nhà cung cấp đã gửi cho bạn.
bash
# CIUS-RO preflight. Sends NOTHING to ANAF.
curl -sS -X POST "https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate?include_xml=true" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# → {"invoice_id":"inv_01J7ABC","valid":false,
#    "errors":["buyer.county is required for RO buyers"],
#    "xml_base64":"PD94bWwg..."}
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Idempotency-Key: efactura-inv_01J7ABC-1'

# → {"submission_id":"sub_01J7XYZ","status":"queued"}
python
import time, requests

BASE = "https://api.4invoices.net/v1"
TERMINAL = {"accepted", "rejected"}
H = {"Authorization": f"Bearer {token}"}

def wait_for_anaf(submission_id, timeout=900):
    deadline = time.time() + timeout
    delay = 15
    while time.time() < deadline:
        r = requests.get(f"{BASE}/efactura/submissions/{submission_id}", headers=H, timeout=30)
        r.raise_for_status()
        status = r.json()["status"]
        if status in TERMINAL:
            return r.json()
        # /refresh asks ANAF directly and shares the 10/min submit bucket.
        requests.post(f"{BASE}/efactura/submissions/{submission_id}/refresh", headers=H, timeout=30)
        time.sleep(delay)
        delay = min(delay * 2, 120)
    raise TimeoutError(submission_id)

Lỗi

  • Mọi lỗi 4xx và 5xx đều dùng cùng một khung: { "error": { type, code, message, param?, doc_url, request_id, errors? } }.
  • type là loại lỗi khái quát mà bạn dùng để rẽ nhánh ("tôi có nên thử lại không?"), còn code là lý do ổn định mà bạn ánh xạ vào hệ thống của mình.
  • param là đường dẫn có dấu chấm đến trường gây lỗi, ví dụ lines.0.vat_rate.
  • errors[] xuất hiện khi một yêu cầu có vấn đề ở nhiều hơn một trường.
  • request_id được ghi nhận phía chúng tôi. Hãy cung cấp mã này trong mọi yêu cầu hỗ trợ — nó xác định chính xác yêu cầu đó.
  • 404 cũng được trả về cho một id thuộc về tenant khác: chúng tôi không bao giờ xác nhận rằng dữ liệu của người khác tồn tại.
json
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_failed",
    "message": "Datele trimise nu sunt valide.",
    "param": "lines.0.vat_rate",
    "doc_url": "https://4invoices.net/ro/api-dezvoltatori#errors",
    "request_id": "req_8a893f2f33d4472da8d6e144",
    "errors": [
      { "param": "lines.0.vat_rate", "message": "VAT rate must be a percent (21), not a fraction", "type": "value_error" }
    ]
  }
}
Trạng tháiLoạiMã
401authentication_errormissing_bearer_token
401authentication_errorinvalid_client
401authentication_errortoken_expired
401authentication_errorinvalid_token
401authentication_errorunauthorized
403permission_errorinsufficient_scope
403permission_errorforbidden
404not_found_errornot_found
409conflict_erroridempotency_key_reuse
409conflict_erroridempotency_in_progress
409conflict_errorno_series_configured
422invalid_request_errorvalidation_failed
422invalid_request_errorissuer_profile_incomplete
422invalid_request_errorcius_ro_incomplete
422invalid_request_errorcannot_storno_draft
422invalid_request_errornothing_to_storno
422invalid_request_errorinvalid_json_number
429rate_limit_errorrate_limit_exceeded
502upstream_errorupstream_error
503service_unavailable_errorservice_unavailable

Giới hạn tần suất

Nhóm giới hạnYêu cầu / phútÝ nghĩa
read120GET *, POST /invoices/preview-totals, POST /invoices/{id}/efactura/validate
write30POST /invoices, POST /invoices/{id}/storno, POST|DELETE /invoices/{id}/payments, POST /clients
submit10POST /invoices/{id}/efactura/submit, POST /efactura/submissions/{id}/refresh, POST /efactura/inbox/sync
  • Giới hạn áp dụng theo từng thông tin xác thực, mỗi phút: 120 cho đọc, 30 cho ghi và 10 cho thao tác với hệ thống hóa đơn điện tử quốc gia (submit, refresh, inbox/sync).
  • X-RateLimit-Limit, X-RateLimit-Remaining và X-RateLimit-Reset xuất hiện trên MỌI phản hồi, kể cả 429.
  • 429 cũng mang Retry-After tính bằng giây. Hãy tuân thủ; đừng thử lại trong vòng lặp dày đặc.
  • Nhóm 10/phút tồn tại vì tại Romania, bản thân ANAF chỉ cho phép khoảng một lệnh gọi danh sách thông điệp mỗi phút trên mỗi CIF.
  • Hãy đặt /efactura/inbox/sync chạy bằng cron, không phải polling dồn dập.
  • Cần mức trần cao hơn? Hãy yêu cầu qua trang Liên hệ, kèm khối lượng hằng ngày dự kiến của bạn.

X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset · Retry-After

http
HTTP/2 429
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785312000
Retry-After: 17
Content-Type: application/json

{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded",
 "message":"...","doc_url":"https://4invoices.net/ro/api-dezvoltatori#errors",
 "request_id":"req_..."}}
javascript
// Tuân thủ Retry-After; không bao giờ thử lại 4xx khác 429.
async function call(url, init, attempt = 0) {
  const res = await fetch(url, init);
  if (res.status === 429 || res.status >= 500) {
    if (attempt >= 5) return res;
    const retryAfter = Number(res.headers.get("Retry-After")) || 2 ** attempt;
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    return call(url, init, attempt + 1);
  }
  return res;
}

// Khi thử lại một lần ghi PHẢI dùng CÙNG Idempotency-Key, nếu không bạn sẽ phát hành
// một chứng từ tài chính thứ hai.

Hướng dẫn theo chủ đề

Mỗi chủ đề có trang riêng với các endpoint, mẫu mã và lỗi liên quan.

Lỗi thường gặp

  • Gửi GTGT dưới dạng phân số (0.19) hoặc dưới dạng 1. Cả hai đều bị từ chối; hãy gửi 19 hoặc 21.
  • Tạo token ở mỗi yêu cầu. Token có hiệu lực một giờ; hãy lưu tạm, nếu không bạn sẽ chạm giới hạn theo IP của endpoint token.
  • Tạo Idempotency-Key mới ở mỗi lần thử lại — chính kịch bản này sinh ra hóa đơn trùng lặp.
  • Gọi từ JavaScript trên trình duyệt. CORS chặn mọi origin, và secret vẫn sẽ bị lộ.
  • Nộp lên hệ thống hóa đơn điện tử quốc gia mà không kiểm tra sơ bộ (CIUS-RO cho Romania). Một lần nộp sai không thể rút lại.
  • Polling /efactura/submissions/{id}/refresh quá 10 lần mỗi phút — nhóm này dùng chung với submit.
  • Phát hành trước khi hồ sơ doanh nghiệp hoàn tất và có dãy số tài chính đang hoạt động.

Câu hỏi và trả lời cho nhà phát triển

Làm thế nào để lấy thông tin xác thực API của 4invoices?

Trong bảng điều khiển, tại Lập hóa đơn → Cài đặt → Truy cập API. Bạn nhận được client_id và client_secret; secret chỉ hiển thị một lần và chỉ được lưu dưới dạng băm.

access_token có hiệu lực trong bao lâu?

3600 giây, tức một giờ. Hãy lưu tạm và làm mới trước khi hết hạn — endpoint token có giới hạn tần suất riêng theo từng IP.

Làm thế nào để gửi thuế suất GTGT?

Dưới dạng phần trăm: 21 cho 21%. Các giá trị trong khoảng (0, 1] bị từ chối, nên cả 0,19 lẫn 1 đều không qua được kiểm tra.

Những lệnh gọi nào yêu cầu Idempotency-Key?

POST /invoices, POST /invoices/{id}/storno và POST /invoices/{id}/efactura/submit. Khóa được ghi nhớ trong 24 giờ, và khi phát lại, hệ thống trả về hóa đơn gốc kèm Idempotency-Replayed: true.

Có thể gửi hóa đơn lên hệ thống hóa đơn điện tử quốc gia trực tiếp từ API không?

Có. Chạy kiểm tra sơ bộ (CIUS-RO cho Romania) bằng /efactura/validate, nộp bằng /efactura/submit, theo dõi trạng thái đã chuẩn hóa tại /efactura/submissions/{id} và tải phản hồi có chữ ký của hệ thống (tại Romania: ANAF).

Giới hạn tần suất là bao nhiêu?

Theo từng thông tin xác thực, mỗi phút: 120 lượt đọc, 30 lượt ghi và 10 thao tác với hệ thống hóa đơn điện tử quốc gia. Mọi phản hồi đều mang các tiêu đề X-RateLimit-*, và phản hồi 429 thêm Retry-After.

Có thể gọi API từ giao diện web (frontend) của tôi không?

Không. API chỉ dành cho máy chủ-tới-máy chủ: không có origin trình duyệt nào vượt qua CORS, và client_secret không được xuất hiện trong mã mà người truy cập có thể thấy.

Vì sao tôi nhận issuer_profile_incomplete ở hóa đơn đầu tiên?

Hồ sơ doanh nghiệp chưa đầy đủ hoặc không có dãy số đang hoạt động thuộc loại fiscale. Hãy hoàn tất cả hai trong bảng điều khiển; API từ chối tạo chứng từ tài chính không hợp lệ.