Aller au contenu
Sursa Developers

Synchroniser votre système avec une source personnalisée

Une source personnalisée connecte un système qui n’a pas de connecteur intégré : votre wiki, votre outil de tickets, une base de données, un export d’où que ce soit. Votre code envoie des documents à Sursa, et Sursa en fait des fiches dans la collection et les dossiers pour lesquels la source est configurée, avec la même recherche, les mêmes réponses, la même validation et les mêmes permissions que les fiches de n’importe quelle autre source.

Contrairement à l’écriture directe de fiches, une source personnalisée garde la trace de ce que vous avez envoyé. À la fin d’une synchronisation complète, Sursa archive chaque document que vous n’avez pas envoyé : les suppressions faites dans votre système parviennent ainsi à Sursa sans travail supplémentaire.

Configurer la source

Un administrateur crée la source dans Sursa sous Sources → New source → Custom source et choisit :

  • la collection et, éventuellement, un dossier pour ses fiches ;
  • le mode des dossiers : toutes les fiches dans le dossier choisi, ou des sous-dossiers qui reproduisent le path de chaque document ;
  • les permissions : celles de la collection, ou « comme dans la source », où chaque document indique qui peut le voir.

La page de la source affiche son identifiant ; les exemples utilisent 42.

Choisir une clé

CléCommence parPeut
Clé de sourcekbsrc_Écrire des documents dans cette seule source. Rien d’autre : elle ne peut ni lire ni rechercher. Créez-la sur la page de la source, sous API keys.
Clé de l’organisation dotée de la portée import:writekb_Écrire dans n’importe quelle source personnalisée de l’organisation et lire son état. La création d’une source et de ses clés exige la portée admin.

La clé de source est le choix le plus sûr pour une intégration unique, surtout si elle tourne sur une plateforme tierce. Une clé de l’organisation dotée de import:write convient à un service qui alimente plusieurs sources.

Les deux clés s’envoient de la même façon, et tous les points de terminaison d’envoi se trouvent sous /v1/push/sources/{source_id}. Vérifiez la clé et l’état de la source avec GET /v1/push/sources/{source_id} :

curl https://api.sursa.com/v1/push/sources/42 \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY"
{"source_id": 42, "name": "Helpdesk articles", "documents": 1840, "open_sync": null}

Le document

ChampObligatoireDescription
external_idouiVotre identifiant stable du document, jusqu’à 512 caractères. Renvoyer le même identifiant remplace le document.
titleouiTitre de la fiche, jusqu’à 500 caractères. Pour un fichier, le nom du fichier par défaut.
contentoui, sauf s’il y a un fichierJusqu’à 5 Mo.
content_typenonhtml (par défaut), markdown ou text. Le HTML est nettoyé des scripts, des styles, des formulaires et des objets intégrés.
urlnonLien vers le document dans votre système, affiché sur la fiche.
pathnonDossiers, sous forme de tableau (["Support", "Billing"]) ou de chaîne ("Support/Billing"), jusqu’à 10 niveaux.
permissionsnon{"everyone": true}, ou {"emails": [...], "groups": [...]}. Utilisé quand les permissions de la source sont « comme dans la source ».
tagsnonJusqu’à 50 étiquettes, ajoutées aux étiquettes de la source.
author_emailnonS’il s’agit d’un utilisateur de l’organisation, il devient le responsable de la fiche.
updated_atnonDate de modification du document dans votre système (RFC 3339).
metadatanonVos paires clé-valeur sur la fiche. Voir Métadonnées.
categorynonCatégorie de la fiche : la clé d’une catégorie de l’organisation.
verifiers, verify_interval_daysnonQui valide la fiche (utilisateurs par adresse e-mail et groupes) et à quelle fréquence, en jours.
upload_idnonLe fichier du document issu d’un téléversement en plusieurs parties, à la place de content.
attachmentsnonJusqu’à 20 fichiers supplémentaires du document, sous forme de liste d’upload_id. Chacun devient une fiche distincte de la source.

À la place de content, un document seul peut porter un fichier d’au plus 100 Mo dans une requête multipart/form-data : le fichier dans la partie file, les champs ci-dessus en JSON dans la partie document. Les téléversements en plusieurs parties pour upload_id et attachments se créent avec une clé de l’organisation dotée de la portée import:write et de la destination import_attachment ; une clé de source ne peut pas les créer.

Deux façons d’envoyer

Les mises à jour directes conviennent aux systèmes qui vous signalent chaque modification, comme un webhook de votre outil de support ou une étape dans Zapier. Envoyez un document quand il est créé ou modifié, et supprimez-le quand il est retiré :

RequêteEffet
POST /v1/push/sources/{source_id}/documentsCrée ou remplace un document.
POST /v1/push/sources/{source_id}/documents/batchCrée ou remplace plusieurs documents à la fois.
DELETE /v1/push/sources/{source_id}/documents/{external_id}Supprime un document. Encodez l’identifiant pour l’URL.

Les modifications deviennent des fiches en une minute environ ; plusieurs modifications successives sont traitées ensemble.

Les synchronisations conviennent aux exports réguliers : vous envoyez tout, ou tout ce qui a changé, et Sursa fait le rapprochement. La suite de ce guide porte sur les synchronisations.

Lancer une synchronisation complète

Une synchronisation compte trois étapes : ouvrir, envoyer, fermer.

1. Ouvrir

POST /v1/push/sources/{source_id}/syncs ouvre une synchronisation et renvoie son numéro :

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Idempotency-Key: sync-2026-10-24" \
  -H "Content-Type: application/json" \
  -d '{"type": "initial", "max_minutes": 180}'
{"sync_number": 7, "type": "initial", "status": "open", "opened_at": "2026-10-24T02:00:00Z",
 "expires_at": "2026-10-24T05:00:00Z", "pushed": 0, "removed": 0}
  • type vaut initial pour la première synchronisation de la source, puis incremental dès qu’une synchronisation s’est terminée. Une synchronisation incremental avant toute synchronisation terminée renvoie sources.push_initial_required.
  • Une seule synchronisation peut être ouverte à la fois : une deuxième renvoie 409 sources.push_sync_open.
  • Une synchronisation reste ouverte pendant max_minutes (60 par défaut, jusqu’à 1440). Ensuite, elle expire : rien n’est archivé et son numéro n’est plus accepté. Choisissez une durée nettement plus longue que celle de votre export.

2. Envoyer les documents

Envoyez les documents par lots d’au plus 500, avec le numéro de la synchronisation. Le corps d’un lot fait au plus 50 Mio ; les fichiers d’un lot sont transmis sous forme d'upload_id.

curl -X POST https://api.sursa.com/v1/push/sources/42/documents/batch \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Idempotency-Key: sync-7-batch-1" \
  -H "Content-Type: application/json" \
  -d '{
    "sync_number": 7,
    "documents": [
      {"external_id": "faq-shipping", "title": "Shipping", "content_type": "markdown",
       "content": "# Shipping\n\nWe ship worldwide within 5 days.", "path": ["Support"]},
      {"external_id": "faq-refunds", "title": "Refunds", "content_type": "html",
       "content": "<p>Refunds take 14 days.</p>", "path": ["Support", "Billing"],
       "permissions": {"groups": ["Support"]}, "metadata": {"article_id": "1042"}}
    ]
  }'

La réponse contient un résultat pour chaque document, dans l’ordre d’envoi :

{
  "results": [
    {"external_id": "faq-shipping", "result": "unchanged", "status": "indexed", "card_id": "8c1d…", "version": "5e0f…"},
    {"external_id": "faq-refunds", "result": "updated", "status": "pending", "card_id": "3f2c…", "version": "a91b…"}
  ]
}
ChampSignification
resultCe qui vient d’arriver au document : created, updated, unchanged, deleted, not_found, error ou skipped (dans un lot atomique qui n’a pas été appliqué).
statusL’étape où se trouve le document sur le chemin qui le mène à une fiche : pending, processing (la fiche est écrite et en cours d’indexation), indexed, failed ou removed.
card_idLa fiche du document, une fois qu’elle existe.
versionUne empreinte du contenu et des champs. Le même document renvoyé a la même version et vaut unchanged.
error, reasonPourquoi le document a échoué. reason est un code stable : content_invalid, file_rejected, too_large, limit_reached, index_failed ou batch_rejected (un autre document d’un lot atomique a échoué).

Par défaut, chaque document d’un lot est appliqué séparément : un document défectueux n’arrête pas les autres.

Les documents qui n’ont pas changé depuis la dernière synchronisation n’ont pas besoin d’être renvoyés. Confirmez-les par identifiant avec POST /v1/push/sources/{source_id}/syncs/{sync_number}/touch, jusqu’à 1000 identifiants par requête, pour que le rapprochement complet les conserve :

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs/7/touch \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_ids": ["faq-contact", "faq-hours"]}'

3. Fermer

POST /v1/push/sources/{source_id}/syncs/{sync_number}/close termine la synchronisation :

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs/7/close \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "complete"}'
statusEffet
completeRapprochement complet. Chaque document de la source qui n’a été ni envoyé ni confirmé pendant cette synchronisation est archivé. Ne l’utilisez que si vous avez tout envoyé.
complete_incrementalApplique ce que vous avez envoyé et n’archive rien.
failedN’archive rien et conserve ce qui a déjà été envoyé. Ajoutez reason (par exemple API_ERROR) et error_details pour le journal.

Fermez avec failed chaque fois que votre export s’arrête en cours de route : un complete après un export partiel archiverait tous les documents que vous n’avez pas eu le temps d’envoyer. Répéter la même fermeture renvoie le même résultat.

Après la fermeture, Sursa transforme les documents en fiches en une minute environ. La page de la source affiche chaque synchronisation avec le nombre de documents envoyés et archivés, et GET /v1/push/sources/{source_id}/syncs/{sync_number} renvoie les mêmes compteurs.

Synchronisations incrémentales

Entre deux synchronisations complètes, n’envoyez que ce qui a changé :

  1. Ouvrez avec "type": "incremental".
  2. Envoyez les documents modifiés et placez les identifiants des documents retirés dans deletes (voir ci-dessous).
  3. Fermez avec complete_incremental.

Lancez de temps en temps une synchronisation complète, par exemple chaque semaine, pour rattraper ce qu’une exécution incrémentale aurait manqué.

Supprimer des documents dans un lot

Un lot peut supprimer des documents en même temps qu’il en écrit d’autres. Listez leurs identifiants dans deletes :

{
  "sync_number": 8,
  "documents": [{"external_id": "faq-shipping", "title": "Shipping", "content": "…"}],
  "deletes": ["faq-old-returns", "faq-fax"]
}

Chaque identifiant supprimé reçoit sa propre entrée dans results, avec result égal à deleted, ou à not_found si la source n’a pas ce document. Supprimer de nouveau le même identifiant est sans conséquence.

Lots atomiques

Certaines modifications n’ont de sens qu’ensemble, par exemple un document et celui qui le remplace. Envoyez "atomic": true : le lot est alors appliqué entièrement ou pas du tout. Si un document échoue à ses vérifications, rien du lot n’est appliqué, et la réponse est 422 avec le code sources.push_batch_rejected et le résultat de chaque document dans error.details.results : les documents qui ont échoué ont error, les autres skipped.

{"sync_number": 8, "atomic": true, "documents": […], "deletes": ["faq-old-returns"]}

Sans atomic, un lot est appliqué document par document. Utilisez les lots atomiques pour les modifications liées, et les lots ordinaires pour les chargements en masse, où un document défectueux ne doit pas retenir les autres.

Vérifier le résultat de chaque document

La réponse au lot vous indique comment chaque document a été écrit. Sa transformation en fiche a lieu ensuite : le status peut donc encore valoir pending. Pour trouver les documents d’une synchronisation qui ne sont pas devenus des fiches :

curl "https://api.sursa.com/v1/push/sources/42/syncs/7/documents?status=failed" \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY"

Chaque entrée a external_id, title, status, card_id, version et reason, et la liste se pagine avec cursor. status accepte aussi pending, processing, indexed et removed.

Vous pouvez aussi être prévenu au lieu de demander : les événements import.sync_completed (une synchronisation est fermée et traitée, avec ses totaux) et import.document_failed (un document n’est pas devenu une fiche, avec son reason) arrivent par webhook et dans le flux d’événements.

Nouvelles tentatives

  • Envoyez un en-tête Idempotency-Key avec chaque POST. Un lot répété avec la même clé renvoie le premier résultat.
  • Même sans la clé, un document qui porte le même external_id ne devient jamais une seconde fiche : il est unchanged ou updated.
  • Si le processus s’arrête au milieu d’une synchronisation, ouvrez-en une nouvelle après l’expiration de l’ancienne, ou fermez l’ancienne avec failed si vous avez encore son numéro, puis relancez la synchronisation.

Limites

LimiteValeur
Contenu d’un document5 Mo
Fichier dans une requête à document unique100 Mo
Documents par lot500
Corps de la requête d’un lot50 Mio
Identifiants par requête touch1000
Documents par sourceSelon l’offre : à partir de 100 000
Synchronisation ouverte60 minutes par défaut, jusqu’à 24 heures

Dans un lot, un document qui dépasse la limite de documents de la source échoue avec le motif limit_reached ; une requête à document unique répond 409 avec le code sources.push_too_many_documents. Les limites de débit s’appliquent comme à toute autre requête ; voir Limites de débit et quotas.

Erreurs courantes

RéponseCauseQue faire
401 sources.push_key_invalidLa clé est erronée, révoquée ou appartient à une autre source.Vérifiez la clé et l’identifiant de la source dans le chemin.
400 sources.push_document_invalidUn champ obligatoire manque ou une limite est dépassée.Corrigez le document ; dans un lot ordinaire, seul ce document échoue.
409 sources.push_sync_openUne autre synchronisation est encore ouverte.Fermez-la, ou attendez qu’elle expire.
409 sources.push_sync_closedLa synchronisation est fermée ou a expiré.Ouvrez une nouvelle synchronisation ; augmentez max_minutes pour les exports longs.
403 sources.feature_disabledLes sources ne font pas partie de l’offre de l’organisation.Contactez l’administrateur.
429Trop de requêtes.Attendez la durée de Retry-After ; envoyez moins de lots, mais plus gros.