← Cài đặt & API key

Hướng dẫn tích hợp API

Tạo job, gửi file và ảnh, theo dõi xử lý và nhận kết quả từ backend của hệ thống tích hợp.

1. Kết nối và bảo vệ API key

  1. Trong Cài đặt & API key, nhập tên hệ thống rồi chọn Tạo API key. Sao chép key ngay: chỉ hiển thị một lần.
  2. Lưu key ở backend của hệ thống tích hợp, trong biến môi trường hoặc kho secret. Không nhúng key vào JavaScript frontend, ứng dụng công khai hoặc URL.
  3. Mỗi request cần hai header dưới đây. Giữ nguyên key và mã user khi tạo, xem và tải kết quả.
Authorization: Bearer <API_KEY>
X-User-Id: <USER_ID>

X-User-Id là mã người dùng do backend của bạn xác định, dài 1–80 ký tự, không chứa ký tự điều khiển. Job được tách theo API key + user. Không tin mã user do frontend gửi mà chưa kiểm tra đăng nhập.

Base URL: https://llm.themultiapp.com. Không dùng cookie hoặc token admin cho các route integration. Khi thu hồi key, mọi request bằng key đó bị từ chối; key mới không tự kế thừa job của key cũ.

2. Các endpoint integration hiện có

Phương thứcĐường dẫnChức năng
POST/api/integration/project-jobsTạo job, JSON hoặc multipart
GET/api/integration/project-jobsDanh sách của user, phân trang/lọc
GET/api/integration/project-jobs/{id}Trạng thái, tiến độ, kết quả
GET/api/integration/project-jobs/{id}/files/{fileId}Tải bytes file đầu ra

Integration hiện chưa có endpoint hủy, chạy lại, liệt kê model hoặc đăng ký webhook. Hủy job và quản lý kết nối/model thực hiện trên UI quản trị. Không dùng các route /api/admin/* để thay thế.

3. Tạo job không có file

Ví dụ chạy ở terminal của backend; đặt VLJ_API_KEY trong môi trường trước. Các mã trong ví dụ đều là dữ liệu minh họa.

curl --fail-with-body 'https://llm.themultiapp.com/api/integration/project-jobs' \
  -H "Authorization: Bearer $VLJ_API_KEY" \
  -H 'X-User-Id: user-123' \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"Tạo báo cáo và lưu report.docx vào thư mục Drive của job.","model":"chatgpt-web/gpt-5.6-sol","retention_minutes":60,"required_outputs":["report.docx"]}'

HTTP 202 xác nhận job đã được ghi nhận; lưu id vào database của hệ thống tích hợp. Đây chưa phải kết quả hoàn tất. Có thể đóng trình duyệt sau khi đã nhận mã.

{"id":"project_...","status":"submitting","created_at":"2026-09-28T02:00:00.000Z","expires_at":null}
TrườngYêu cầuÝ nghĩa
promptBắt buộcMục tiêu, cách phân tích, số lượng/định dạng đầu ra; yêu cầu lưu kết quả vào folder Drive của job.
modelTùy chọnID model hiện có trong UI; bỏ qua để dùng model mặc định. Khả năng tạo ảnh và công cụ phụ thuộc model/tài khoản.
retention_minutesTùy chọn, số nguyên 1–10080Thời gian lưu từ lúc hoàn tất, mặc định theo Cài đặt (thường 60 phút).
required_outputsTùy chọn, mảng tên fileMỗi tên tối đa 160 ký tự, không có đường dẫn. Chỉ báo hoàn tất khi có đủ file không rỗng. Ví dụ: ["report.docx","result.xlsx"].
filesChỉ multipartLặp lại trường này cho từng tài liệu hoặc ảnh.

4. Gửi nhiều file, ảnh hoặc tạo nhiều đầu ra

File và ảnh dùng cùng endpoint. Để curl hoặc FormData tự đặt multipart boundary; không tự đặt Content-Type khi dùng FormData.

curl --fail-with-body 'https://llm.themultiapp.com/api/integration/project-jobs' \
  -H "Authorization: Bearer $VLJ_API_KEY" \
  -H 'X-User-Id: user-123' \
  -F 'prompt=Đọc tài liệu và ảnh, tạo report.docx và result.json trong thư mục Drive của job.' \
  -F 'model=chatgpt-web/gpt-5.6-sol' \
  -F 'retention_minutes=60' \
  -F 'required_outputs=["report.docx","result.json"]' \
  -F 'files=@./document.pdf' \
  -F 'files=@./photo.png'

Upload API: không giới hạn số tệp, 64 MiB mỗi file; UI giới hạn 64 MiB tổng để tải thuận tiện. Proxy có thể áp thêm giới hạn request. JSON body tối đa 4 MiB; gửi file qua multipart, không chèn base64 vào prompt.

ZIP được backend kiểm tra và giải nén trước khi giao worker, kể cả ZIP lồng tối đa 3 cấp. Tổng tối đa 128 MiB giải nén, mỗi file tối đa 64 MiB; từ chối ZIP có mật khẩu, liên kết, đường dẫn nguy hiểm, trùng tên hoặc lỗi CRC. Cây thư mục và ZIP gốc được giữ trong thư mục job. request.json → input_files cung cấp ID, archive_chain, relative_path và prepared_path cho file đã giải nén. Worker đọc các file này trực tiếp; kết quả vẫn ghi ở gốc thư mục job. Dữ liệu giải nén được dọn cùng job theo TTL hiện tại.

Để tạo nhiều ảnh: mô tả từng ảnh trong prompt, yêu cầu lưu riêng từng file vào folder của job và liệt kê tên trong required_outputs, ví dụ ["front.png","side.png"]. Không có trường mode riêng. Việc gửi yêu cầu không bảo đảm model có công cụ tạo ảnh; kiểm tra lỗi và file thực tế trả về.

5. Danh sách, tiến độ và hàng đợi

curl --fail-with-body 'https://llm.themultiapp.com/api/integration/project-jobs?limit=25&offset=0&status=processing&q=report' \
  -H "Authorization: Bearer $VLJ_API_KEY" -H 'X-User-Id: user-123'

curl --fail-with-body 'https://llm.themultiapp.com/api/integration/project-jobs/project_EXAMPLE' \
  -H "Authorization: Bearer $VLJ_API_KEY" -H 'X-User-Id: user-123'

Danh sách trả jobs, total, counts, limit, offset. limit từ 1–100, mặc định 25; offset bắt đầu 0. q tìm trong yêu cầu/mã job; status lọc một trạng thái. counts tính toàn bộ job của user, không theo bộ lọc.

Tối đa 5 job đồng thời trên hệ thống; vượt số này sẽ chờ. Worker chạy trên server, không phụ thuộc tab hay vòng polling. Poll GET khoảng 3–5 giây; nếu mất kết nối, tăng khoảng chờ và dùng lại ID đã lưu. Tiến độ chuẩn progress.contract_version=2 gồm checkpoint.phase, completed_steps, next_action, completed_units, total_units, percent (chỉ khi đo được), last_activity_at, idle_timeout_seconds, idle_deadline_at, completion_verified. Chỉ nhận hoàn tất khi status=completed và completion_verified=true; file xuất hiện chưa đủ. Timeout tính từ thay đổi cuối đã xác minh của checkpoint/kết quả, không từ số lần poll hoặc heartbeat. Giữ mã job khi mất kết nối; không gửi lại yêu cầu. Saved chat chỉ tiếp tục phần còn thiếu khi có receipt sở hữu và lượt trước đã dừng; Temporary không được mở lại bằng ID của saved chat.

Trạng tháiỨng dụng nên làm gì
preparing / submitting / queuedĐang chuẩn bị hoặc chờ; tiếp tục theo dõi.
claimed / processingĐang nhận/xử lý; hiển thị tiến độ.
retry_waitServer tự thử lại; không tạo job trùng.
finalizingĐang nhận/kiểm chứng file; chưa tải như kết quả cuối.
completedĐọc artifacts, tải và lưu trước expires_at.
failed / cancelledDừng polling, hiển thị error; không báo thành công.
expired / cleanup_pendingHết hạn hoặc đang dọn; không còn được tải kết quả.

POST integration hiện không có hợp đồng idempotency. Nếu POST bị timeout sau khi gửi, tra danh sách trước khi tạo lại; không tự lặp POST vô hạn. Retry nội bộ của worker không yêu cầu phía tích hợp tạo job mới.

6. Nhận và tải kết quả

{
  "id": "project_EXAMPLE", "status": "completed",
  "expires_at": "2026-09-28T03:00:00.000Z",
  "artifacts": [{"id":"FILE_ID","name":"report.docx","size":"12345",
    "mimeType":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "download_url":"/api/integration/project-jobs/project_EXAMPLE/files/FILE_ID"}]
}
curl --fail-with-body \
  'https://llm.themultiapp.com/api/integration/project-jobs/project_EXAMPLE/files/FILE_ID' \
  -H "Authorization: Bearer $VLJ_API_KEY" -H 'X-User-Id: user-123' \
  --output report.docx

Dùng artifacts[].download_url từ response, không tự suy ra Drive URL. Đường dẫn tương đối cần ghép với base URL. Endpoint trả bytes, MIME và Content-Disposition; giới hạn tải 64 MiB mỗi file. Một job có thể có nhiều artifacts, hãy duyệt toàn bộ danh sách.

Link cần header xác thực, không phải link công khai để dán thẳng vào trình duyệt. Backend của bạn kiểm tra user đăng nhập rồi proxy bytes về frontend; không đưa API key vào query string. Tải về kho của hệ thống tích hợp trước expires_at. Sau TTL, API trả 410 và folder/file được dọn trên Drive; thời gian chờ/chạy không trừ TTL.

7. Ví dụ JavaScript chạy ở backend (Node.js 22)

import { writeFile } from 'node:fs/promises';
const base = 'https://llm.themultiapp.com';
const headers = { Authorization: `Bearer ${process.env.VLJ_API_KEY}`,
  'X-User-Id': 'user-123' }; // Lấy từ user đã xác thực ở backend của bạn.
async function request(path, options = {}) {
  const response = await fetch(base + path, {
    ...options, headers: { ...headers, ...options.headers },
    signal: AbortSignal.timeout(120000)
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  return response;
}
const created = await (await request('/api/integration/project-jobs', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ prompt: 'Tạo result.txt chứa HELLO trong folder Drive của job.',
    required_outputs: ['result.txt'], retention_minutes: 60 })
})).json();
// Lưu created.id vào DB trước khi theo dõi. Khi backend khởi động lại, dùng ID này.
let job;
for (;;) {
  job = await (await request(`/api/integration/project-jobs/${created.id}`)).json();
  if (job.status === 'completed') break;
  if (['failed','cancelled','expired','cleanup_pending'].includes(job.status))
    throw new Error(JSON.stringify(job.error || { status: job.status }));
  await new Promise(resolve => setTimeout(resolve, 5000));
}
for (const [index, file] of job.artifacts.entries()) {
  const url = new URL(file.download_url, base);
  if (url.origin !== base) throw new Error('Unexpected download origin');
  const response = await request(url.pathname + url.search);
  // Tên cục bộ do ứng dụng quyết định, không dùng tên file bên ngoài làm đường dẫn.
  await writeFile(`result-${index}.bin`, Buffer.from(await response.arrayBuffer()));
}

Đây là ví dụ tối giản. Trong production, lưu ID/trạng thái trong DB, chạy polling bằng worker nền và retry lỗi mạng/5xx có backoff. Frontend chỉ đọc dữ liệu từ backend của bạn; không giữ job sống bằng tab trình duyệt.

8. Lỗi và kiểm tra kết nối

HTTP / errorCách xử lý
400Kiểm tra X-User-Id, prompt, retention_minutes, required_outputs và dữ liệu upload. Đọc trường error.
401 integration_api_key_requiredKey thiếu, sai hoặc bị thu hồi; không thay bằng token admin.
404ID sai, khác key/user, hoặc file không thuộc output của job.
410 job_expiredKết quả đã hết hạn; không retry tải file này.
413File/request quá lớn; giảm dung lượng.
502 / lỗi mạngThử lại GET có backoff. Với POST, kiểm tra danh sách trước khi gửi lại.
required_outputs_missingModel xong nhưng thiếu file bắt buộc. Xem error.missing; đây là job thất bại.
google_drive_not_connectedQuản trị kết nối lại Google Drive tại Cài đặt.

Kiểm tra key bằng GET danh sách với đúng hai header; HTTP 200 và danh sách rỗng vẫn là kết nối hợp lệ. Khi tích hợp Vietland12 hoặc hệ thống khác, tách key theo hệ thống và giữ user ID ổn định.

Tạo ảnh và nhận file qua Drive

Gửi mode=image, image_count=1 (1–4), và prompt qua cùng endpoint tạo project job. ChatGPT tạo ảnh; server lấy bytes ảnh, lưu vào thư mục Drive của job rồi công bố file tải. Job chỉ hoàn tất khi đủ số ảnh. mode=auto nhận diện yêu cầu tạo ảnh; mode=drive dùng cho phân tích và tài liệu. Chế độ tạo ảnh hỗ trợ tệp tham chiếu. Khi có ảnh/PDF/DOCX/XLSX/CSV/TXT hoặc dữ liệu khác, job vẫn dùng cùng thư mục Google Drive: worker đọc và chuẩn hóa ràng buộc từ input trước, sau đó image worker tạo ảnh và server công bố file ảnh chỉ sau khi đã upload thành công vào chính thư mục job. Model chọn trên form áp dụng cho bước đọc/phân tích dữ liệu tham chiếu; bước sinh ảnh dùng image worker.