Importare documenti
Questa guida mostra come inserire i tuoi documenti in Sursa con l’API delle schede: pochi documenti da uno script, oppure tutti i documenti di un sistema con un job periodico. Ogni documento diventa una scheda in una raccolta. Le persone la trovano nella ricerca, l’assistente risponde in base a essa, e ha un responsabile, tag, verifica e permessi come qualsiasi altra scheda.
Se vuoi che sia Sursa a capire quali documenti sono stati eliminati nel tuo sistema, o se invii documenti da una piattaforma di integrazione, usa invece una fonte personalizzata. È pensata per sincronizzazioni complete e incrementali. Questa guida tratta le scritture dirette in una raccolta.
Ti serve una chiave API con l’ambito cards:write e un ruolo che possa scrivere nella raccolta di destinazione
(editor o admin). Gli esempi usano la funzione di supporto dell’Avvio rapido.
Scegliere l’endpoint giusto
| Hai | Usa |
|---|---|
| Un record con un ID stabile nel tuo sistema | PUT /v1/cards-by-external-id/{external_id}: crea o sostituisce in base al tuo ID. |
| Un testo da aggiungere una volta, senza un tuo ID | POST /v1/cards/text |
| Un file da aggiungere una volta | POST /v1/cards |
| Un archivio ZIP con molti file | POST /v1/cards/archive |
| Un file più grande del limite di una singola richiesta | Un caricamento in parti, poi uno qualsiasi degli endpoint precedenti. |
| Un nuovo contenuto per una scheda che hai già | PUT /v1/cards/{card_id}/content |
Per tutto ciò che importi più di una volta, usa external_id. Rende ripetibile ogni importazione: eseguirla di nuovo
aggiorna le schede che ha creato invece di aggiungerne copie.
Creare o sostituire una scheda con il tuo ID
PUT /v1/cards-by-external-id/{external_id} accetta l’ID del record nel tuo sistema. Se nessuna scheda ha questo
external_id, ne crea una (201). Se una scheda lo ha, ne sostituisce il contenuto e i dettagli (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"}
}'Cosa succede quando ripeti la richiesta:
- Il contenuto non è cambiato: non viene creata una nuova versione. La risposta è
200con"status": "skipped"e"reason": "unchanged". Ripetere la richiesta è sempre sicuro, anche senza unaIdempotency-Key. - Il contenuto è cambiato: la scheda riceve una nuova versione e viene indicizzata di nuovo. Titolo, tag,
responsabile, categoria e
metadataassumono i valori che hai inviato;metadataviene sostituito per intero. - La raccolta e la cartella si applicano solo quando la scheda viene creata. Per spostare una scheda in seguito,
modificala con
PATCH /v1/cards/{card_id}.
Una nuova versione rimuove la verifica della scheda, come qualsiasi modifica del contenuto: un verificatore la conferma di nuovo.
Formati di testo
content_type | Il corpo è |
|---|---|
markdown (predefinito) | Markdown. |
html | HTML. Viene ridotto a un markup semplice: titoli, paragrafi, elenchi, tabelle e link http, https e mailto. Script, stili, frame, moduli e immagini vengono rimossi insieme al loro contenuto. |
text | Testo semplice. I paragrafi sono separati da righe vuote. |
Attendere la scheda, oppure no
Per impostazione predefinita, la richiesta risponde quando la scheda è pronta per essere trovata, con
"status": "ready" e la scheda in card. Ci vuole da una frazione di secondo per un testo breve a un minuto o più per
un file lungo.
Per le importazioni massive, invia "async": true. La risposta arriva subito con "status": "queued" e il job di
indicizzazione in job (con il suo job_id). Controlla il job con GET /v1/jobs/{job_id},
oppure segui tutti i job in GET /v1/jobs/stream. Un job termina con status done,
error o canceled.
curl https://api.sursa.com/v1/jobs/48213 \
-H "Authorization: Bearer $SURSA_API_KEY"Caricare un file
I file si inviano in una richiesta multipart/form-data, nel campo file. Sursa legge PDF, Word, Excel, PowerPoint,
fogli di calcolo OpenDocument, CSV, Markdown, HTML, AsciiDoc, e-mail (.eml), testo semplice e i formati di immagine più
comuni. Controlla che nome, tipo dichiarato e contenuto del file coincidano e rifiuta una discrepanza con 415.
Per creare o sostituire una scheda con un file in base al tuo ID, invia il file allo stesso
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"}'In un modulo, tags è una stringa separata da virgole e metadata è un oggetto JSON scritto come stringa. Una nuova
scheda senza title riceve come titolo il nome del file.
Per aggiungere un file una sola volta, senza un tuo ID, usa POST /v1/cards con lo
stesso modulo e una Idempotency-Key. Caricare un file che la raccolta ha già restituisce "status": "skipped" con
"duplicate": true e il card_id esistente, a meno che tu non passi un external_id.
La dimensione di un singolo file è limitata dall’installazione (200 MB per impostazione predefinita). Per file più grandi, o con reti poco affidabili, carica prima il file in parti.
Sostituire il contenuto di una scheda
Quando hai già l’ID di Sursa di una scheda, PUT /v1/cards/{card_id}/content
ne sostituisce il testo o il file e mantiene tutto il resto: responsabile, tag, verificatori, intervallo di verifica e
una categoria impostata manualmente.
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."}'- Lo stesso contenuto inviato di nuovo restituisce
"status": "skipped"con"reason": "unchanged". - Una scheda in corso di indicizzazione restituisce
409con il codicecards.indexing_in_progress. Riprova tra qualche secondo. - Una scheda che proviene da una fonte, come un connettore o una fonte personalizzata, si modifica solo nella sua fonte:
cards.managed_by_source. - La chiave deve poter vedere l’intera scheda; una scheda visibile solo in parte restituisce
cards.content_whole_access.
Importare molti documenti
Un ciclo sui tuoi record con PUT /v1/cards-by-external-id/… è l’importazione massiva più semplice, e si può fermare
ed eseguire di nuovo in qualsiasi momento senza rischi. Per renderla veloce e rispettosa del limite di frequenza:
- Invia
"async": true, così ogni richiesta risponde non appena la scheda è in coda. - Esegui alcune richieste in parallelo (da 4 a 8) e rallenta quando l’intestazione
RateLimitmostra che restano poche richieste. Vedi Limiti di frequenza e quote. - Con un
429, attendi il tempo diRetry-After; con un errore di rete o un5xx, ritenta la stessa richiesta. Le ripetizioni non hanno conseguenze. - Annota l’
external_iddi ogni record che termina con un errore e ritenta quei record alla fine.
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;
}Per un archivio ZIP di file, POST /v1/cards/archive trasforma in una scheda,
con una sola richiesta, ogni file supportato che contiene. Risponde subito con il piano (quali file diventano schede e
quali vengono saltati, e perché) e un job; GET /v1/archives/{job_id}
riporta il risultato.
Trovare e rimuovere le schede importate
- Leggi una scheda in base al tuo ID con
GET /v1/cards-by-external-id/{external_id}. - Elenca le schede di un’importazione con un filtro sui metadati:
GET /v1/cards?metadata[source]=helpdesk. Vedi Metadati. - Elimina una scheda con
DELETE /v1/cards/{card_id}.
Errori comuni
| Codice | Causa | Cosa fare |
|---|---|---|
collections.not_found | Nessuna raccolta con questo ID o nome è visibile alla chiave. | Controlla collection_id con GET /v1/collections. |
collections.no_write_access | La chiave non può scrivere in questa raccolta. | Usa una chiave con il ruolo editor e accesso alla raccolta. |
cards.external_id_taken | POST con un external_id che un’altra scheda ha già. | Usa PUT /v1/cards-by-external-id/… per sostituirla. |
cards.managed_by_source | La scheda appartiene a una fonte. | Modifica il documento nella sua fonte. |
files.type_mismatch | Nome, tipo e contenuto del file non coincidono. | Invia il file con la sua estensione reale. |
files.too_large | Il file supera il limite di caricamento o lo spazio di archiviazione dell’organizzazione. | Usa un caricamento in parti oppure libera spazio di archiviazione. |
limits.cards | L’organizzazione ha raggiunto il limite di schede del suo piano. | Elimina le schede che non servono più oppure cambia piano. |
api.metadata_invalid | metadata supera un limite. | Vedi Metadati. |