Przejdź do treści
Sursa Developers

Importuj dokumenty

Ten przewodnik pokazuje, jak umieścić dokumenty w Sursie za pomocą API kart: kilka dokumentów ze skryptu albo wszystkie dokumenty systemu w ramach regularnego zadania. Każdy dokument staje się kartą w kolekcji. Ludzie znajdują ją w wyszukiwaniu, asystent odpowiada na jej podstawie, a karta ma właściciela, tagi, weryfikację i uprawnienia jak każda inna karta.

Jeśli chcesz, aby Sursa sama ustalała, które dokumenty zostały usunięte w Twoim systemie, lub wysyłasz dokumenty z platformy integracyjnej, użyj zamiast tego własnego źródła. Jest ono przeznaczone do pełnych i przyrostowych synchronizacji. Ten przewodnik omawia bezpośredni zapis do kolekcji.

Potrzebujesz klucza API z zakresem cards:write i rolą, która może zapisywać w docelowej kolekcji (editor lub admin). Przykłady używają funkcji pomocniczej z przewodnika Szybki start.

Wybierz właściwą operację

MaszUżyj
Rekord ze stałym identyfikatorem w Twoim systemiePUT /v1/cards-by-external-id/{external_id}: utworzenie lub zastąpienie według Twojego identyfikatora.
Tekst do jednorazowego dodania, bez własnego identyfikatoraPOST /v1/cards/text
Plik do jednorazowego dodaniaPOST /v1/cards
Archiwum ZIP z wieloma plikamiPOST /v1/cards/archive
Plik większy niż limit pojedynczego żądaniaPrzesyłanie w częściach, a następnie dowolna z powyższych operacji.
Nowa treść dla istniejącej kartyPUT /v1/cards/{card_id}/content

Do wszystkiego, co importujesz więcej niż raz, używaj external_id. Dzięki niemu każdy import można powtórzyć: ponowne uruchomienie aktualizuje utworzone wcześniej karty zamiast dodawać kopie.

Utwórz lub zastąp kartę według własnego identyfikatora

PUT /v1/cards-by-external-id/{external_id} przyjmuje identyfikator rekordu w Twoim systemie. Jeśli żadna karta nie ma tego external_id, operacja tworzy nową kartę (201). Jeśli karta go ma, operacja zastępuje treść i szczegóły karty (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"}
  }'

Co się dzieje przy powtórzeniu:

  • Treść się nie zmieniła: nie powstaje nowa wersja. Odpowiedź to 200 z "status": "skipped" i "reason": "unchanged". Powtórzenie żądania jest zawsze bezpieczne, nawet bez Idempotency-Key.
  • Treść się zmieniła: karta otrzymuje nową wersję i jest ponownie indeksowana. Jej tytuł, tagi, właściciel, kategoria i metadata przyjmują wysłane wartości; metadata jest zastępowane w całości.
  • Kolekcja i folder są brane pod uwagę tylko przy tworzeniu karty. Aby później przenieść kartę, zmień ją za pomocą PATCH /v1/cards/{card_id}.

Nowa wersja usuwa weryfikację karty, jak każda zmiana treści: weryfikator potwierdza ją ponownie.

Formaty tekstu

content_typeTreść żądania to
markdown (domyślnie)Markdown.
htmlHTML. Jest oczyszczany do prostych znaczników: nagłówków, akapitów, list, tabel oraz linków http, https i mailto. Skrypty, style, ramki, formularze i obrazy są usuwane razem z zawartością.
textZwykły tekst. Akapity są oddzielone pustymi wierszami.

Poczekaj na kartę albo nie

Domyślnie żądanie kończy się, gdy kartę można już znaleźć, ze statusem "status": "ready" i kartą w card. Trwa to od ułamka sekundy w przypadku krótkiego tekstu do minuty lub dłużej w przypadku długiego pliku.

Przy imporcie masowym wysyłaj "async": true. Odpowiedź przychodzi od razu z "status": "queued" i zadaniem indeksowania w job (z jego job_id). Sprawdzaj zadanie za pomocą GET /v1/jobs/{job_id} lub śledź wszystkie zadania w GET /v1/jobs/stream. Zadanie kończy się wartością status równą done, error lub canceled.

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

Prześlij plik

Pliki wysyła się w żądaniu multipart/form-data, w polu file. Sursa odczytuje pliki PDF, Word, Excel, PowerPoint, arkusze OpenDocument, CSV, Markdown, HTML, AsciiDoc, wiadomości e-mail (.eml), zwykły tekst i popularne formaty obrazów. Sprawdza, czy nazwa pliku, zadeklarowany typ i zawartość są zgodne, a niezgodność odrzuca z kodem 415.

Aby utworzyć lub zastąpić kartę z plikiem według własnego identyfikatora, wyślij plik na ten sam adres 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"}'

W formularzu tags to ciąg wartości oddzielonych przecinkami, a metadata to obiekt JSON zapisany jako ciąg znaków. Nowa karta bez title otrzymuje jako tytuł nazwę pliku.

Aby dodać plik jednorazowo, bez własnego identyfikatora, użyj POST /v1/cards z tym samym formularzem i nagłówkiem Idempotency-Key. Przesłanie pliku, który kolekcja już zawiera, zwraca "status": "skipped" z "duplicate": true i istniejącym card_id, chyba że przekażesz external_id.

Rozmiar pojedynczego pliku jest ograniczony przez instalację (domyślnie 200 MB). W przypadku większych plików lub zawodnych sieci najpierw prześlij plik w częściach.

Zastąp treść karty

Gdy masz już identyfikator karty w Sursie, PUT /v1/cards/{card_id}/content zastępuje jej tekst lub plik i zachowuje wszystko inne: właściciela, tagi, weryfikatorów, interwał weryfikacji i kategorię ustawioną ręcznie.

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."}'
  • Ta sama treść wysłana ponownie zwraca "status": "skipped" z "reason": "unchanged".
  • Karta, która jest właśnie indeksowana, zwraca 409 z kodem cards.indexing_in_progress. Ponów żądanie za kilka sekund.
  • Kartę pochodzącą ze źródła, np. z konektora lub własnego źródła, można zmienić tylko w jej źródle: cards.managed_by_source.
  • Klucz musi widzieć całą kartę; karta widoczna tylko częściowo zwraca cards.content_whole_access.

Importuj wiele dokumentów

Pętla po Twoich rekordach z PUT /v1/cards-by-external-id/… to najprostszy import masowy, który można bezpiecznie zatrzymać i uruchomić ponownie w dowolnym momencie. Aby był szybki i mieścił się w limicie częstotliwości:

  1. Wysyłaj "async": true, aby każde żądanie kończyło się, gdy tylko karta trafi do kolejki.
  2. Uruchamiaj kilka żądań równolegle (od 4 do 8) i zwalniaj, gdy nagłówek RateLimit pokazuje, że zostało niewiele żądań. Zob. Limity częstotliwości i przydziały.
  3. Przy 429 odczekaj czas z Retry-After; przy błędzie sieci lub 5xx ponów to samo żądanie. Powtórzenia są nieszkodliwe.
  4. Zapisuj external_id każdego rekordu, którego import zakończył się błędem, i ponów te rekordy na końcu.
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;
}

W przypadku archiwum ZIP z plikami POST /v1/cards/archive jednym żądaniem zamienia w kartę każdy obsługiwany plik z archiwum. Odpowiada od razu planem (które pliki staną się kartami, które zostaną pominięte i dlaczego) oraz zadaniem; GET /v1/archives/{job_id} podaje wynik.

Znajdź i usuń zaimportowane karty

Typowe błędy

KodPrzyczynaCo zrobić
collections.not_foundKlucz nie widzi kolekcji o tym ID ani o tej nazwie.Sprawdź collection_id za pomocą GET /v1/collections.
collections.no_write_accessKlucz nie może zapisywać w tej kolekcji.Użyj klucza z rolą editor i dostępem do kolekcji.
cards.external_id_takenPOST z external_id, który ma już inna karta.Użyj PUT /v1/cards-by-external-id/…, aby ją zastąpić.
cards.managed_by_sourceKarta należy do źródła.Zmień dokument w jego źródle.
files.type_mismatchNazwa, typ i zawartość pliku są niezgodne.Wyślij plik z jego prawdziwym rozszerzeniem.
files.too_largePlik przekracza limit przesyłania lub miejsca na dane organizacji.Użyj przesyłania w częściach albo zwolnij miejsce.
limits.cardsOrganizacja osiągnęła limit kart w swoim planie.Usuń niepotrzebne karty lub zmień plan.
api.metadata_invalidmetadata przekracza limit.Zob. Metadane.