Documenten importeren
Deze handleiding laat zien hoe je je documenten met de API voor kaarten in Sursa zet: een paar documenten vanuit een script, of elk document van een systeem via een regelmatige job. Elk document wordt een kaart in een collectie. Mensen vinden hem bij het zoeken, de assistent antwoordt op basis ervan, en hij heeft een eigenaar, tags, verificatie en rechten zoals elke andere kaart.
Wil je dat Sursa zelf uitzoekt welke documenten in je systeem zijn verwijderd, of verstuur je documenten vanuit een integratieplatform, gebruik dan een eigen bron. Die is gebouwd voor volledige en incrementele synchronisaties. Deze handleiding behandelt het rechtstreeks schrijven naar een collectie.
Je hebt een API-sleutel nodig met de scope cards:write en een rol die naar de doelcollectie mag schrijven (editor
of admin). De voorbeelden gebruiken de hulpfunctie uit de Snelstart.
Het juiste endpoint kiezen
| Je hebt | Gebruik |
|---|---|
| Een record met een stabiele ID in je systeem | PUT /v1/cards-by-external-id/{external_id}: maken of vervangen op je eigen ID. |
| Tekst die je één keer toevoegt, zonder eigen ID | POST /v1/cards/text |
| Een bestand dat je één keer toevoegt | POST /v1/cards |
| Een ZIP-archief met veel bestanden | POST /v1/cards/archive |
| Een bestand dat groter is dan de limiet voor één request | Een upload in delen, daarna een van de endpoints hierboven. |
| Nieuwe inhoud voor een kaart die je al hebt | PUT /v1/cards/{card_id}/content |
Gebruik external_id voor alles wat je meer dan één keer importeert. Daarmee is elke import herhaalbaar: opnieuw
uitvoeren werkt de kaarten bij die de import heeft gemaakt, in plaats van kopieën toe te voegen.
Een kaart maken of vervangen op je eigen ID
PUT /v1/cards-by-external-id/{external_id} neemt de ID van het record in je systeem aan. Heeft geen enkele kaart
deze external_id, dan wordt er een gemaakt (201). Heeft een kaart hem wel, dan worden de inhoud en de gegevens van
die kaart vervangen (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"}
}'Wat er gebeurt bij een herhaling:
- De inhoud is niet veranderd: er wordt geen nieuwe versie gemaakt. De response is
200met"status": "skipped"en"reason": "unchanged". Het request herhalen is altijd veilig, ook zonderIdempotency-Key. - De inhoud is veranderd: de kaart krijgt een nieuwe versie en wordt opnieuw geïndexeerd. De titel, tags,
eigenaar, categorie en
metadatakrijgen de waarden die je hebt gestuurd;metadatawordt in zijn geheel vervangen. - De collectie en de map gelden alleen bij het maken van de kaart. Om een kaart later te verplaatsen, wijzig je hem
met
PATCH /v1/cards/{card_id}.
Een nieuwe versie haalt de verificatie van de kaart weg, zoals elke wijziging van de inhoud: een controleur bevestigt hem opnieuw.
Tekstformaten
content_type | De body is |
|---|---|
markdown (standaard) | Markdown. |
html | HTML. Die wordt opgeschoond tot eenvoudige opmaak: koppen, alinea’s, lijsten, tabellen en links met http, https en mailto. Scripts, stijlen, frames, formulieren en afbeeldingen worden met hun inhoud verwijderd. |
text | Platte tekst. Alinea’s worden gescheiden door lege regels. |
Op de kaart wachten, of niet
Standaard geeft het request antwoord zodra de kaart vindbaar is, met "status": "ready" en de kaart in card. Dat duurt
van een fractie van een seconde voor een korte tekst tot een minuut of langer voor een lang bestand.
Stuur bij imports in bulk "async": true mee. De response komt dan meteen, met "status": "queued" en de
indexeringsjob in job (met zijn job_id). Controleer de job met GET /v1/jobs/{job_id}
of volg alle jobs in GET /v1/jobs/stream. Een job eindigt met status done,
error of canceled.
curl https://api.sursa.com/v1/jobs/48213 \
-H "Authorization: Bearer $SURSA_API_KEY"Een bestand uploaden
Bestanden gaan in een multipart/form-data-request, in het veld file. Sursa leest PDF, Word, Excel, PowerPoint,
OpenDocument-spreadsheets, CSV, Markdown, HTML, AsciiDoc, e-mail (.eml), platte tekst en gangbare
afbeeldingsformaten. Het controleert of de naam, het opgegeven type en de inhoud van het bestand overeenkomen, en
weigert een afwijking met 415.
Om een bestandskaart te maken of te vervangen op je eigen ID, stuur je het bestand naar hetzelfde
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 een formulier is tags een string met door komma’s gescheiden waarden en is metadata een JSON-object dat als
string is geschreven. Een nieuwe kaart zonder title krijgt de bestandsnaam als titel.
Om een bestand één keer toe te voegen, zonder eigen ID, gebruik je POST /v1/cards
met hetzelfde formulier en een Idempotency-Key. Upload je een bestand dat de collectie al heeft, dan krijg je
"status": "skipped" terug met "duplicate": true en de bestaande card_id, tenzij je een external_id meegeeft.
De grootte van één bestand wordt begrensd door de installatie (standaard 200 MB). Upload grotere bestanden, of bestanden over onbetrouwbare netwerken, eerst in delen.
De inhoud van een kaart vervangen
Heb je de Sursa-ID van een kaart al, dan vervangt PUT /v1/cards/{card_id}/content
de tekst of het bestand ervan en blijft al het andere behouden: eigenaar, tags, controleurs, verificatie-interval en
een met de hand ingestelde categorie.
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."}'- Dezelfde inhoud nog een keer geeft
"status": "skipped"met"reason": "unchanged". - Een kaart die op dit moment wordt geïndexeerd, geeft
409met de codecards.indexing_in_progress. Probeer het over een paar seconden opnieuw. - Een kaart die uit een bron komt, zoals een connector of een eigen bron, wordt alleen in die bron gewijzigd:
cards.managed_by_source. - De sleutel moet de hele kaart mogen zien; een kaart die maar gedeeltelijk zichtbaar is, geeft
cards.content_whole_access.
Veel documenten importeren
Een lus over je records met PUT /v1/cards-by-external-id/… is de eenvoudigste import in bulk, en je kunt hem op elk
moment veilig stoppen en opnieuw uitvoeren. Zo maak je hem snel en vriendelijk voor de snelheidslimiet:
- Stuur
"async": truemee, zodat elk request terugkomt zodra de kaart in de wachtrij staat. - Voer een paar requests parallel uit (4 tot 8) en vertraag als de header
RateLimitlaat zien dat er nog maar weinig requests over zijn. Zie Snelheidslimieten en quota. - Wacht bij
429de tijd uitRetry-Afteraf; probeer bij een netwerkfout of5xxhetzelfde request opnieuw. Herhalingen kunnen geen kwaad. - Noteer de
external_idvan elk record dat op een fout uitloopt, en probeer die aan het eind opnieuw.
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;
}Voor een ZIP-archief met bestanden maakt POST /v1/cards/archive met één
request van elk ondersteund bestand erin een kaart. Het antwoordt meteen met het plan (welke bestanden kaarten worden
en welke worden overgeslagen, en waarom) en een job; GET /v1/archives/{job_id}
meldt het resultaat.
Geïmporteerde kaarten vinden en verwijderen
- Lees een kaart op je eigen ID met
GET /v1/cards-by-external-id/{external_id}. - Toon de kaarten van één import met een filter op metadata:
GET /v1/cards?metadata[source]=helpdesk. Zie Metadata. - Verwijder een kaart met
DELETE /v1/cards/{card_id}.
Veelvoorkomende fouten
| Code | Oorzaak | Wat je doet |
|---|---|---|
collections.not_found | Er is geen collectie met deze ID of naam zichtbaar voor de sleutel. | Controleer collection_id met GET /v1/collections. |
collections.no_write_access | De sleutel mag niet naar deze collectie schrijven. | Gebruik een sleutel met de rol editor en toegang tot de collectie. |
cards.external_id_taken | POST met een external_id die een andere kaart al heeft. | Gebruik PUT /v1/cards-by-external-id/… om hem te vervangen. |
cards.managed_by_source | De kaart hoort bij een bron. | Wijzig het document in zijn bron. |
files.type_mismatch | De naam, het type en de inhoud van het bestand komen niet overeen. | Stuur het bestand met zijn echte extensie. |
files.too_large | Het bestand gaat boven de uploadlimiet of de opslag van de organisatie uit. | Gebruik een upload in delen of maak opslag vrij. |
limits.cards | De organisatie heeft de kaartlimiet van haar abonnement bereikt. | Verwijder kaarten die niet meer nodig zijn, of wijzig het abonnement. |
api.metadata_invalid | metadata overschrijdt een limiet. | Zie Metadata. |