Chuyển tới nội dung
Sursa Developers

Nhập tài liệu

Hướng dẫn này trình bày cách đưa tài liệu của bạn vào Sursa bằng API thẻ: vài tài liệu từ một script, hoặc mọi tài liệu của một hệ thống qua một tác vụ chạy định kỳ. Mỗi tài liệu trở thành một thẻ trong một bộ sưu tập. Mọi người tìm thấy nó khi tìm kiếm, trợ lý trả lời dựa trên nó, và nó có người phụ trách, nhãn, kiểm chứng và quyền như mọi thẻ khác.

Nếu bạn muốn Sursa tự xác định tài liệu nào đã bị xóa trong hệ thống của bạn, hoặc bạn đẩy tài liệu từ một nền tảng tích hợp, hãy dùng nguồn tùy chỉnh. Nó được xây dựng cho đồng bộ toàn bộ và đồng bộ gia tăng. Hướng dẫn này trình bày việc ghi trực tiếp vào một bộ sưu tập.

Bạn cần một khóa API có phạm vi quyền cards:write và vai trò được ghi vào bộ sưu tập đích (editor hoặc admin). Các ví dụ dùng hàm hỗ trợ từ Bắt đầu nhanh.

Chọn endpoint phù hợp

Bạn cóHãy dùng
Một bản ghi có ID ổn định trong hệ thống của bạnPUT /v1/cards-by-external-id/{external_id}: tạo hoặc thay thế theo ID của bạn.
Văn bản cần thêm một lần, không có ID riêngPOST /v1/cards/text
Một tệp cần thêm một lầnPOST /v1/cards
Một tệp nén ZIP chứa nhiều tệpPOST /v1/cards/archive
Một tệp lớn hơn giới hạn của một yêu cầuTải lên theo từng phần, rồi dùng bất kỳ endpoint nào ở trên.
Nội dung mới cho một thẻ đã cóPUT /v1/cards/{card_id}/content

Với bất cứ thứ gì bạn nhập nhiều lần, hãy dùng external_id. Nó làm cho mọi lượt nhập có thể lặp lại: chạy lại sẽ cập nhật các thẻ đã tạo thay vì thêm bản sao.

Tạo hoặc thay thế thẻ theo ID của bạn

PUT /v1/cards-by-external-id/{external_id} nhận ID của bản ghi trong hệ thống của bạn. Nếu chưa có thẻ nào có external_id này, endpoint tạo một thẻ (201). Nếu đã có, nó thay thế nội dung và thông tin chi tiết của thẻ (200).

curl -X PUT https://api.sursa.com/v1/cards-by-external-id/kb-article-1042 \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "collection_id": 3,
    "folder_id": "0f8e2d7c-4b6a-4e1f-9c3d-2a5b8e7f1c40",
    "title": "Returning a damaged item",
    "content_type": "html",
    "body": "<h2>Damaged items</h2><p>Send a photo within 7 days and we ship a replacement.</p>",
    "tags": ["returns", "shipping"],
    "metadata": {"source": "helpdesk", "article_id": "1042"}
  }'

Điều gì xảy ra khi lặp lại:

  • Nội dung không thay đổi: không có phiên bản mới nào được tạo. Phản hồi là 200 với "status": "skipped" và "reason": "unchanged". Lặp lại yêu cầu luôn an toàn, ngay cả khi không có Idempotency-Key.
  • Nội dung đã thay đổi: thẻ có phiên bản mới và được lập chỉ mục lại. Tiêu đề, nhãn, người phụ trách, danh mục và metadata của thẻ nhận các giá trị bạn gửi; metadata được thay thế toàn bộ.
  • Bộ sưu tập và thư mục chỉ áp dụng khi thẻ được tạo. Để chuyển thẻ sau này, hãy thay đổi nó bằng PATCH /v1/cards/{card_id}.

Phiên bản mới gỡ bỏ trạng thái kiểm chứng của thẻ, giống như mọi thay đổi nội dung: người kiểm chứng xác nhận lại thẻ.

Định dạng văn bản

content_typePhần thân là
markdown (mặc định)Markdown.
htmlHTML. Nó được làm sạch thành đánh dấu đơn giản: tiêu đề, đoạn văn, danh sách, bảng và liên kết http, https và mailto. Script, style, frame, biểu mẫu và hình ảnh bị loại bỏ cùng với nội dung của chúng.
textVăn bản thuần. Các đoạn văn được phân tách bằng dòng trống.

Chờ thẻ hoặc không

Theo mặc định, yêu cầu trả về khi thẻ đã sẵn sàng để được tìm thấy, với "status": "ready" và thẻ trong card. Việc đó mất từ một phần nhỏ của giây với văn bản ngắn tới một phút hoặc hơn với tệp dài.

Với lượt nhập hàng loạt, hãy gửi "async": true. Phản hồi tới ngay với "status": "queued" và tác vụ lập chỉ mục trong job (kèm job_id). Kiểm tra tác vụ bằng GET /v1/jobs/{job_id}, hoặc theo dõi mọi tác vụ trong GET /v1/jobs/stream. Tác vụ kết thúc với status là done, error hoặc canceled.

curl https://api.sursa.com/v1/jobs/48213 \
  -H "Authorization: Bearer $SURSA_API_KEY"

Tải tệp lên

Tệp được gửi trong yêu cầu multipart/form-data, ở trường file. Sursa đọc được PDF, Word, Excel, PowerPoint, bảng tính OpenDocument, CSV, Markdown, HTML, AsciiDoc, email (.eml), văn bản thuần và các định dạng hình ảnh phổ biến. Sursa kiểm tra rằng tên tệp, loại được khai báo và nội dung tệp khớp nhau, và từ chối trường hợp không khớp với 415.

Để tạo hoặc thay thế thẻ tệp theo ID của bạn, hãy gửi tệp tới cùng endpoint PUT /v1/cards-by-external-id/{external_id}:

curl -X PUT https://api.sursa.com/v1/cards-by-external-id/handbook-2026 \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  -F "file=@employee-handbook.pdf" \
  -F "collection_id=3" \
  -F "tags=hr,policies" \
  -F 'metadata={"owner_team":"people"}'

Trong biểu mẫu, tags là chuỗi phân tách bằng dấu phẩy và metadata là đối tượng JSON được viết dưới dạng chuỗi. Thẻ mới không có title lấy tên tệp làm tiêu đề.

Để thêm một tệp một lần, không có ID riêng, hãy dùng POST /v1/cards với cùng biểu mẫu và một Idempotency-Key. Tải lên một tệp mà bộ sưu tập đã có trả về "status": "skipped" với "duplicate": true và card_id của thẻ hiện có, trừ khi bạn truyền external_id.

Kích thước của một tệp bị giới hạn theo bản cài đặt (mặc định 200 MB). Với tệp lớn hơn, hoặc với mạng không ổn định, trước tiên hãy tải tệp lên theo từng phần.

Thay thế nội dung của thẻ

Khi bạn đã có ID Sursa của thẻ, PUT /v1/cards/{card_id}/content thay thế văn bản hoặc tệp của thẻ và giữ nguyên mọi thứ khác: người phụ trách, nhãn, người kiểm chứng, chu kỳ kiểm chứng và danh mục được đặt thủ công.

curl -X PUT https://api.sursa.com/v1/cards/3f2c9a7e-5b1d-4c8e-9f0a-1b2c3d4e5f60/content \
  -H "Authorization: Bearer $SURSA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content_type": "markdown", "body": "# Refund policy\n\nRefunds are processed within 10 days."}'
  • Gửi lại cùng nội dung trả về "status": "skipped" với "reason": "unchanged".
  • Thẻ đang được lập chỉ mục ngay lúc đó trả về 409 với mã cards.indexing_in_progress. Hãy thử lại sau vài giây.
  • Thẻ đến từ một nguồn, chẳng hạn trình kết nối hoặc nguồn tùy chỉnh, chỉ được thay đổi trong nguồn của nó: cards.managed_by_source.
  • Khóa phải được phép xem toàn bộ thẻ; thẻ chỉ hiển thị một phần trả về cards.content_whole_access.

Nhập nhiều tài liệu

Một vòng lặp qua các bản ghi của bạn với PUT /v1/cards-by-external-id/… là cách nhập hàng loạt đơn giản nhất, và có thể dừng rồi chạy lại an toàn ở bất kỳ thời điểm nào. Để nhập nhanh và không vượt giới hạn tốc độ:

  1. Gửi "async": true, để mỗi yêu cầu trả về ngay khi thẻ được đưa vào hàng đợi.
  2. Chạy song song một vài yêu cầu (từ 4 đến 8), và giảm tốc khi header RateLimit cho thấy còn ít yêu cầu. Xem Giới hạn tốc độ và hạn mức.
  3. Khi gặp 429, hãy chờ hết Retry-After; khi có lỗi mạng hoặc 5xx, hãy thử lại cùng yêu cầu. Lặp lại không gây hại.
  4. Ghi lại external_id của từng bản ghi kết thúc bằng lỗi, và thử lại chúng ở cuối.
import { api } from "./sursa.mjs";

async function importAll(records, concurrency = 6) {
  const failed = [];
  const queue = [...records];
  async function worker() {
    for (let record = queue.shift(); record; record = queue.shift()) {
      try {
        await api("PUT", `/v1/cards-by-external-id/${encodeURIComponent(record.id)}`, {
          collection_id: 3,
          title: record.title,
          content_type: "html",
          body: record.html,
          metadata: { source: "helpdesk" },
          async: true,
        });
      } catch (error) {
        failed.push({ id: record.id, error: String(error) });
      }
    }
  }
  await Promise.all(Array.from({ length: concurrency }, worker));
  return failed;
}

Với tệp nén ZIP chứa nhiều tệp, POST /v1/cards/archive biến mỗi tệp được hỗ trợ bên trong thành một thẻ chỉ với một yêu cầu. Endpoint trả lời ngay với kế hoạch (tệp nào trở thành thẻ, tệp nào bị bỏ qua và vì sao) và một tác vụ; GET /v1/archives/{job_id} báo cáo kết quả.

Tìm và xóa thẻ đã nhập

Lỗi thường gặp

MãNguyên nhânCách xử lý
collections.not_foundKhóa không nhìn thấy bộ sưu tập nào có ID hoặc tên này.Kiểm tra collection_id bằng GET /v1/collections.
collections.no_write_accessKhóa không được ghi vào bộ sưu tập này.Dùng khóa có vai trò editor và quyền truy cập bộ sưu tập.
cards.external_id_takenPOST với external_id mà một thẻ khác đã có.Dùng PUT /v1/cards-by-external-id/… để thay thế thẻ đó.
cards.managed_by_sourceThẻ thuộc về một nguồn.Thay đổi tài liệu trong nguồn của nó.
files.type_mismatchTên, loại và nội dung tệp không khớp nhau.Gửi tệp với phần mở rộng thật của nó.
files.too_largeTệp vượt giới hạn tải lên hoặc dung lượng lưu trữ của tổ chức.Dùng tải lên theo từng phần, hoặc giải phóng dung lượng lưu trữ.
limits.cardsTổ chức đã chạm giới hạn số thẻ của gói.Xóa những thẻ không còn cần, hoặc đổi gói.
api.metadata_invalidmetadata vượt một giới hạn.Xem Siêu dữ liệu.