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ę
| Masz | Użyj |
|---|---|
| Rekord ze stałym identyfikatorem w Twoim systemie | PUT /v1/cards-by-external-id/{external_id}: utworzenie lub zastąpienie według Twojego identyfikatora. |
| Tekst do jednorazowego dodania, bez własnego identyfikatora | POST /v1/cards/text |
| Plik do jednorazowego dodania | POST /v1/cards |
| Archiwum ZIP z wieloma plikami | POST /v1/cards/archive |
| Plik większy niż limit pojedynczego żądania | Przesyłanie w częściach, a następnie dowolna z powyższych operacji. |
| Nowa treść dla istniejącej karty | PUT /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
200z"status": "skipped"i"reason": "unchanged". Powtórzenie żądania jest zawsze bezpieczne, nawet bezIdempotency-Key. - Treść się zmieniła: karta otrzymuje nową wersję i jest ponownie indeksowana. Jej tytuł, tagi, właściciel,
kategoria i
metadataprzyjmują wysłane wartości;metadatajest 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_type | Treść żądania to |
|---|---|
markdown (domyślnie) | Markdown. |
html | HTML. 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ą. |
text | Zwykł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
409z kodemcards.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:
- Wysyłaj
"async": true, aby każde żądanie kończyło się, gdy tylko karta trafi do kolejki. - Uruchamiaj kilka żądań równolegle (od 4 do 8) i zwalniaj, gdy nagłówek
RateLimitpokazuje, że zostało niewiele żądań. Zob. Limity częstotliwości i przydziały. - Przy
429odczekaj czas zRetry-After; przy błędzie sieci lub5xxponów to samo żądanie. Powtórzenia są nieszkodliwe. - Zapisuj
external_idkaż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
- Odczytaj kartę według własnego identyfikatora za pomocą
GET /v1/cards-by-external-id/{external_id}. - Wyświetl karty jednego importu za pomocą filtra metadanych:
GET /v1/cards?metadata[source]=helpdesk. Zob. Metadane. - Usuń kartę za pomocą
DELETE /v1/cards/{card_id}.
Typowe błędy
| Kod | Przyczyna | Co zrobić |
|---|---|---|
collections.not_found | Klucz nie widzi kolekcji o tym ID ani o tej nazwie. | Sprawdź collection_id za pomocą GET /v1/collections. |
collections.no_write_access | Klucz nie może zapisywać w tej kolekcji. | Użyj klucza z rolą editor i dostępem do kolekcji. |
cards.external_id_taken | POST z external_id, który ma już inna karta. | Użyj PUT /v1/cards-by-external-id/…, aby ją zastąpić. |
cards.managed_by_source | Karta należy do źródła. | Zmień dokument w jego źródle. |
files.type_mismatch | Nazwa, typ i zawartość pliku są niezgodne. | Wyślij plik z jego prawdziwym rozszerzeniem. |
files.too_large | Plik przekracza limit przesyłania lub miejsca na dane organizacji. | Użyj przesyłania w częściach albo zwolnij miejsce. |
limits.cards | Organizacja osiągnęła limit kart w swoim planie. | Usuń niepotrzebne karty lub zmień plan. |
api.metadata_invalid | metadata przekracza limit. | Zob. Metadane. |