Vai al contenuto
Sursa Developers

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

HaiUsa
Un record con un ID stabile nel tuo sistemaPUT /v1/cards-by-external-id/{external_id}: crea o sostituisce in base al tuo ID.
Un testo da aggiungere una volta, senza un tuo IDPOST /v1/cards/text
Un file da aggiungere una voltaPOST /v1/cards
Un archivio ZIP con molti filePOST /v1/cards/archive
Un file più grande del limite di una singola richiestaUn 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 è 200 con "status": "skipped" e "reason": "unchanged". Ripetere la richiesta è sempre sicuro, anche senza una Idempotency-Key.
  • Il contenuto è cambiato: la scheda riceve una nuova versione e viene indicizzata di nuovo. Titolo, tag, responsabile, categoria e metadata assumono i valori che hai inviato; metadata viene 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_typeIl corpo è
markdown (predefinito)Markdown.
htmlHTML. 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.
textTesto 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 409 con il codice cards.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:

  1. Invia "async": true, così ogni richiesta risponde non appena la scheda è in coda.
  2. Esegui alcune richieste in parallelo (da 4 a 8) e rallenta quando l’intestazione RateLimit mostra che restano poche richieste. Vedi Limiti di frequenza e quote.
  3. Con un 429, attendi il tempo di Retry-After; con un errore di rete o un 5xx, ritenta la stessa richiesta. Le ripetizioni non hanno conseguenze.
  4. Annota l’external_id di 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

Errori comuni

CodiceCausaCosa fare
collections.not_foundNessuna raccolta con questo ID o nome è visibile alla chiave.Controlla collection_id con GET /v1/collections.
collections.no_write_accessLa chiave non può scrivere in questa raccolta.Usa una chiave con il ruolo editor e accesso alla raccolta.
cards.external_id_takenPOST con un external_id che un’altra scheda ha già.Usa PUT /v1/cards-by-external-id/… per sostituirla.
cards.managed_by_sourceLa scheda appartiene a una fonte.Modifica il documento nella sua fonte.
files.type_mismatchNome, tipo e contenuto del file non coincidono.Invia il file con la sua estensione reale.
files.too_largeIl file supera il limite di caricamento o lo spazio di archiviazione dell’organizzazione.Usa un caricamento in parti oppure libera spazio di archiviazione.
limits.cardsL’organizzazione ha raggiunto il limite di schede del suo piano.Elimina le schede che non servono più oppure cambia piano.
api.metadata_invalidmetadata supera un limite.Vedi Metadati.