Les notes dans Codenica API

Pour travailler avec les notes via Codenica API, commencez par créer une clé dans les paramètres de Codenica. Si vous n'en avez pas encore créé, ouvrez dans un nouvel onglet Codenica API - introduction. Cette page présente les règles communes de création des clés, de conservation du secret et d'authentification des requêtes.

Le nom technique du module est notes et le type d'un objet individuel est note. Une note est une entrée enregistrée dans Codenica. Elle peut contenir un titre, une description, un statut, une priorité, une catégorie, un lien et des fichiers. Le champ isPrivate contrôle la visibilité selon les autorisations existantes, tandis que pin définit le niveau d'épinglage de l'entrée.

Les sections suivantes présentent l'adresse de l'API, les scopes, le contexte, le schéma, les champs, les listes, la recherche, la création, l'idempotence, l'ETag, la modification, l'épinglage, les relations, l'auteur, les fichiers, les opérations batch et la suppression.

Les exemples utilisent le préfixe PUBLIC-API-NOTE-20260905141812. Remplacez-le par votre propre identifiant et adaptez les adresses, les identifiants et les valeurs de champs aux données de votre base.


Notes - adresse de l'API et choix de l'installation

Toutes les routes des notes commencent par :

{BASE_URL}/api/v1/notes

BASE_URL est l'adresse du serveur Codenica sans le suffixe final /api/v1. Pour Codenica Cloud, utilisez le domaine public attribué à l'entreprise concernée :

export BASE_URL="https://votre-entreprise.codenica.com"

Dans l'installation On-Premise par défaut, l'adresse enregistrée localement par Codenica Discovery est :

export BASE_URL="http://codenica.local:5150"

Si l'administrateur a publié l'installation sous un domaine d'entreprise, via un reverse proxy, avec HTTPS ou sur un autre port, utilisez l'adresse exacte qui vous a été communiquée :

export BASE_URL="https://api.votre-entreprise.example"

N'utilisez pas localhost si le programme d'intégration s'exécute sur un autre ordinateur que l'API. La base de données utilisée est déterminée par l'adresse à laquelle l'intégration se connecte. N'envoyez pas tenantId dans le body ni dans la query string.


Notes - clé d'API et limites de licence

Créez une clé d'API dans Codenica, dans Paramètres - API - API Keys. Le secret n'est affiché qu'une seule fois, juste après la création ou la rotation de la clé. À ce moment-là, enregistrez le Client ID et le Client Secret dans le stockage sécurisé utilisé par l'intégration.

Codenica API est disponible avec les licences Plus et Enterprise. Plus permet de créer jusqu'à 50 clés actives, tandis qu'Enterprise en permet jusqu'à 100. Starter n'inclut pas Codenica API. Créez une clé distincte pour chaque application et environnement afin de gérer séparément ses scopes, de faire tourner son secret ou de supprimer son accès.

Licence
Accès à l'API
Nombre maximal de clés actives
Starter
Non disponible
0
Plus
Disponible
50
Enterprise
Disponible
100

La suppression d'une clé supprime son enregistrement et libère une place dans la limite. Lorsque sa date d'expiration est dépassée, la clé ne permet plus l'authentification, mais reste dans la liste jusqu'à sa suppression. Si aucune date de fin n'est définie lors de la création, la durée de validité par défaut est de 90 jours. La durée maximale de validité d'une clé est de 5 ans.


Notes - authentification et requêtes sécurisées

Authentifiez chaque requête Codenica API avec les deux en-têtes de la clé :

export CLIENT_ID="cna_votre_client_id"
export CLIENT_SECRET="cns_votre_client_secret"

curl --request GET --url "$BASE_URL/api/v1/notes?page=1&pageSize=25" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Une intégration externe n'a pas besoin du JWT de l'administrateur ni des cookies du panneau Codenica. Ne placez pas la clé dans un dépôt, dans du code envoyé au navigateur, dans une URL, dans l'historique du shell ou dans les journaux. Utilisez HTTPS en dehors des tests locaux.

Conservez meta.requestId dans la réponse. Il identifie une requête précise pour le diagnostic, mais ce n'est pas l'identifiant de la note et il ne doit pas être traité comme un secret.


Notes - vérifier le contexte de connexion

Avant le premier enregistrement, lisez le contexte. Il confirme que l'adresse pointe vers la base souhaitée et que la clé choisie possède les scopes nécessaires :

curl --request GET --url "$BASE_URL/api/v1/context" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Vérifiez les valeurs suivantes dans la réponse :

  • data.apiVersion et data.contractVersion ;
  • data.tenant.id, data.tenant.name et data.tenant.resolvedDomain ;
  • data.caller.authentication égal à api_key ;
  • la présence de notes dans data.capabilities.resources ;
  • les scopes attribués à la clé ;
  • les limites de pages, de relations, de fichiers et de requêtes.

Si le contexte indique une autre base ou ne contient pas un scope requis, arrêtez l'intégration et corrigez l'adresse ou la clé. Les scopes ne peuvent pas être ajoutés à une requête individuelle.


Notes - scopes et autorisations

La prise en charge complète des notes nécessite les scopes correspondant aux opérations utilisées par votre intégration :

notes:read
notes:write
notes:delete
notes:schema
notes:stats
notes:relationships:read
notes:relationships:write
notes:users:read
notes:files:read
notes:files:write
notes:technical:read
notes:technical:write
notes:pin:write

Pour une lecture ordinaire, notes:read suffit. Le schéma et les statistiques utilisent respectivement notes:schema et notes:stats. Pour créer, modifier et supprimer, ajoutez notes:write et notes:delete selon le besoin.

  • notes:relationships:read et notes:relationships:write couvrent les relations entre objets ;
  • notes:users:read permet de lire l'auteur ;
  • notes:files:read et notes:files:write couvrent la liste, le téléchargement, l'envoi, l'association et la suppression des fichiers ;
  • notes:stats couvre les statistiques et les valeurs de champs utilisées pour les filtres ;
  • notes:pin:write est nécessaire pour épingler et désépingler ;
  • utilisez les scopes techniques uniquement si l'intégration a besoin de champs marqués comme techniques dans le schéma ou dans les règles customValues.

Si l'intégration recherche elle-même les cibles des relations, accordez aussi les scopes de lecture correspondants, par exemple assets:read, clients:read, vendors:read, documents:read, tickets:read, changes:read, problems:read, releases:read, approvals:read, confirmations:read, worktasks:read et requesteditems:read. Les scopes de la clé ne remplacent pas les autorisations de l'utilisateur ni l'accès à une localisation ou à un service.


Notes - schéma et champs

Le schéma indique quels champs peuvent être lus et écrits dans la base sélectionnée. Récupérez-le avant de créer un formulaire ou une correspondance de champs :

curl --request GET --url "$BASE_URL/api/v1/notes/schema" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

La réponse contient notamment data.itemType, data.fields et data.relationshipTargets. La valeur fixe de itemType pour ce module est note. Pour chaque champ, vérifiez readable, writable, required, technical, unique et maxLength. Ne construisez pas votre correspondance uniquement à partir des exemples de cet article, car la configuration des champs peut varier d'une base à l'autre.

Le schéma indique également si les relations avec un ensemble donné sont disponibles. Utilisez uniquement les cibles renvoyées pour la clé et l'utilisateur actuels.


Notes - champs modifiables et champs système

Le catalogue public des champs métier des Notes comprend :

customId
location
department
isPrivate
tag
link
title
status
priority
category
description

Les principales limites de champs sont les suivantes :

Champ
Type
Longueur maximale
customId
string
500
location, department
string
300 chacun
isPrivate
boolean
-
tag, link
string
2000 chacun
title
string
1000
status, priority, category
string
300 chacun
description
string
10000

pin est renvoyé dans les attributs, mais ne peut pas être modifié par attributes. Utilisez son endpoint dédié. Les champs système et techniques en lecture seule comprennent notamment :

id
itemType
pin
creator
updater
dateCreated
dateUpdated
importId
importSource
dateImported

id et itemType font partie de la ressource. Les dates, l'auteur et le dernier éditeur sont attribués par le système. N'essayez pas de les modifier via attributes.


Notes - endpoints principaux

Les routes principales du module notes sont :

GET    /api/v1/notes
POST   /api/v1/notes
GET    /api/v1/notes/{NOTE_ID}
PATCH  /api/v1/notes/{NOTE_ID}
DELETE /api/v1/notes/{NOTE_ID}
GET    /api/v1/notes/schema
GET    /api/v1/notes/stats
GET    /api/v1/notes/values
POST   /api/v1/notes:batch
GET    /api/v1/notes/{NOTE_ID}/relationships
POST   /api/v1/notes/{NOTE_ID}/relationships
POST   /api/v1/notes/{NOTE_ID}/relationships:batch
DELETE /api/v1/notes/{NOTE_ID}/relationships/{DATASET}/{TARGET_ID}
GET    /api/v1/notes/{NOTE_ID}/user-relationships
GET    /api/v1/notes/{NOTE_ID}/files
POST   /api/v1/notes/{NOTE_ID}/files
POST   /api/v1/notes/{NOTE_ID}/files/{FILE_ID}
DELETE /api/v1/notes/{NOTE_ID}/files/{FILE_ID}
GET    /api/v1/notes/{NOTE_ID}/files/{FILE_ID}/content
POST   /api/v1/notes/{NOTE_ID}/pin

Toutes les routes nécessitent une authentification. Les opérations qui modifient les données nécessitent également Idempotency-Key, et les opérations protégées par version nécessitent l'If-Match actuel. Vérifiez les exigences exactes dans les réponses du contexte et du schéma.


Notes - liste et pagination

La liste est paginée. Cet exemple récupère la première page et trie les notes de la plus récente à la plus ancienne :

curl --request GET --url "$BASE_URL/api/v1/notes?itemType=note&page=1&pageSize=20&sort=dateCreated&direction=desc" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

La réponse contient notamment :

data.items
data.page
data.pageSize
data.totalItems
data.totalPages
data.hasNextPage

La valeur maximale de pageSize est indiquée dans le contexte et vaut normalement 100. Récupérez les pages suivantes tant que data.hasNextPage vaut true. Ne supposez pas que le nombre d'enregistrements de la première page est la liste complète.


Notes - recherche, filtres et tri

Vous pouvez utiliser des filtres d'égalité sur des champs tels que customId, location, department, isPrivate, tag, link, title, status, priority et category. Cet exemple recherche les notes privées ouvertes du service IT :

curl --request GET --url "$BASE_URL/api/v1/notes?isPrivate=true&department=IT&status=Open&page=1&pageSize=50" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

search recherche dans les champs textuels de la note, notamment customId, tag, link, title, status, priority, category et description :

curl --request GET --url "$BASE_URL/api/v1/notes?search=integration&page=1&pageSize=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Le paramètre filter peut être répété. Son format est field:operator:value :

filter=status:eq:Open
filter=status:ne:Closed
filter=title:contains:server
filter=title:startswith:Public API
filter=isPrivate:eq:true
filter=description:notempty:

Les opérateurs pris en charge comprennent eq, ne, gt, gte, lt, lte, contains, startswith, endswith, empty et notempty. Les raccourcis comprennent =, !=, ge, le, sw et ew. Vous pouvez ajouter createdAfter, createdBefore, updatedAfter et updatedBefore. Triez uniquement sur un champ autorisé par le schéma, avec direction=asc ou direction=desc. Encodez dans l'URL les valeurs contenant des espaces ou des caractères spéciaux.


Notes - sélection des champs et données incluses

Si l'intégration n'a besoin que d'une partie des données, limitez la réponse avec fields :

curl --request GET --url "$BASE_URL/api/v1/notes?fields=customId%2Ctitle%2Cstatus%2Cpriority%2CisPrivate&page=1&pageSize=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Vous pouvez récupérer une note avec ses fichiers, ses relations et les informations sur son auteur :

curl --request GET --url "$BASE_URL/api/v1/notes/{NOTE_ID}?fields=customId%2Ctitle%2Cdescription%2Cstatus&include=files%2Crelationships%2Cusers" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Les valeurs include autorisées sont files, relationships et users. Chacune nécessite son scope de lecture correspondant. fields=* demande tous les champs accessibles à la clé, mais les champs techniques ne sont renvoyés que si le scope technique correspondant est accordé.


Notes - statistiques et valeurs de champs

Les statistiques comptent les notes visibles et les regroupent selon un champ :

curl --request GET --url "$BASE_URL/api/v1/notes/stats?field=category&limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Exemple de réponse :

{
  "data": {
    "total": 42,
    "field": "category",
    "values": [
      {
        "value": "Integration",
        "count": 12
      },
      {
        "value": "Hardware",
        "count": 8
      }
    ]
  },
  "meta": {
    "requestId": "request-id-from-response"
  }
}

Sans paramètre field, l'endpoint renvoie le nombre total de notes. limit accepte des valeurs de 1 à 500. Les résultats respectent la visibilité de l'utilisateur.

L'endpoint values renvoie les valeurs distinctes utiles pour construire des listes de sélection :

curl --request GET --url "$BASE_URL/api/v1/notes/values?field=status&search=Open&limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Exemple de réponse :

{
  "data": {
    "field": "status",
    "values": [
      "Open",
      "Open - waiting"
    ]
  },
  "meta": {
    "requestId": "request-id-from-response"
  }
}

Ces deux endpoints sont en lecture seule et ne modifient pas les notes. La réponse de values n'est pas une liste d'enregistrements, mais une liste de valeurs distinctes pour un champ donné.


Notes - créer un enregistrement

Placez le type technique note dans le body et les champs métier dans attributes. Dans une intégration réelle, il est préférable d'enregistrer un titre et une description même lorsque le schéma ne les indique pas comme obligatoires :

{
  "itemType": "note",
  "attributes": {
    "customId": "NOTE-ERP-2026-0001",
    "location": "Warsaw",
    "department": "IT",
    "isPrivate": true,
    "tag": "erp,public-api,notes",
    "link": "https://erp.example.com/notes/0001",
    "title": "Server integration check",
    "status": "Open",
    "priority": "Normal",
    "category": "Integration",
    "description": "Note created by an external ERP system."
  }
}

Enregistrez le body sous note-create.json et envoyez-le avec une clé d'idempotence unique :

curl --request POST --url "$BASE_URL/api/v1/notes" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: notes-create-20260905-0001" \
  --data-binary @note-create.json

Une création réussie renvoie 201 Created. La réponse contient l'UUID dans data.id, data.itemType=note, les attributs enregistrés, les dates système et data.meta.etag. Laissez le système attribuer id.

isPrivate contrôle la visibilité, mais ne constitue pas un chiffrement. N'enregistrez pas de mots de passe, de tokens, de Client Secret ni d'autres données confidentielles dans une note.


Notes - répéter la création en toute sécurité

Si le client ne sait pas si la première requête est arrivée, répétez exactement la même requête avec le même Idempotency-Key :

curl --request POST --url "$BASE_URL/api/v1/notes" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: notes-create-20260905-0001" \
  --data-binary @note-create.json

La répétition de la même requête logique ne doit pas créer une deuxième note. La réponse doit indiquer le même UUID et le même résultat d'opération. Ne réutilisez pas cette clé pour un autre body, endpoint ou opération. Toute nouvelle mutation doit recevoir un nouvel Idempotency-Key.

Après un timeout, ne créez pas immédiatement un autre enregistrement. Répétez d'abord la requête précédente avec le même body et la même clé d'idempotence.


Notes - lire un enregistrement et gérer l'ETag

Après la création ou avant une modification, récupérez une note et enregistrez son UUID ainsi que son ETag actuel :

export NOTE_ID="d7a83ba0-41ce-44f6-b2e9-7ddcec716234"

curl --request GET --url "$BASE_URL/api/v1/notes/$NOTE_ID" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

L'ETag apparaît dans l'en-tête HTTP ETag, dans data.meta.etag et dans l'enveloppe meta.etag. Exemple de réponse pour une ressource :

{
  "data": {
    "id": "d7a83ba0-41ce-44f6-b2e9-7ddcec716234",
    "itemType": "note",
    "attributes": {
      "customId": "NOTE-ERP-0001",
      "title": "Server integration check",
      "isPrivate": true
    },
    "meta": {
      "customId": "NOTE-ERP-0001",
      "etag": "\"etag-value\""
    }
  },
  "meta": {
    "requestId": "request-id-from-response",
    "etag": "\"etag-value\""
  }
}

Après chaque mutation réussie, l'ETag peut changer, y compris après une modification de relation, de fichier ou de pin. Remplacez l'ancienne valeur avant la modification suivante.


Notes - mise à jour partielle avec If-Match

PATCH ne modifie que les champs envoyés dans attributes. Utilisez l'ETag actuel et une clé d'idempotence distincte :

export NOTE_ETAG='"etag-from-the-latest-response"'

curl --request PATCH --url "$BASE_URL/api/v1/notes/$NOTE_ID" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-update-20260905-0001" \
  --data-raw '{
    "attributes": {
      "title": "Updated server integration check",
      "description": "The note was changed by an API workflow.",
      "status": "In progress",
      "priority": "High",
      "isPrivate": false
    }
  }'

Il n'est pas nécessaire d'envoyer tous les champs. Vous pouvez effacer une valeur facultative avec null si le schéma de la base l'autorise :

{
  "attributes": {
    "link": null,
    "description": null
  }
}

Un PATCH vide, sans attributs, règles de valeurs ni modification de relation, est rejeté. Les champs système et pin ne font pas partie d'une mise à jour ordinaire.


Notes - ETag obsolète et If-Match manquant

Les mutations des Notes exigent l'en-tête If-Match. Sans cet en-tête, l'API renvoie 428 Precondition Required :

{
  "type": "https://docs.codenica.com/errors/if_match_required",
  "title": "Precondition required.",
  "status": 428,
  "detail": "Send the ETag returned by GET in the If-Match header.",
  "instance": "/api/v1/notes/{id}",
  "code": "if_match_required",
  "requestId": "request-id-from-response"
}

Si l'ETag envoyé est obsolète, l'API renvoie 412 Precondition Failed :

{
  "type": "https://docs.codenica.com/errors/if_match_failed",
  "title": "Precondition failed.",
  "status": 412,
  "detail": "The supplied ETag is not the current note version.",
  "instance": "/api/v1/notes/{id}",
  "code": "if_match_failed",
  "requestId": "request-id-from-response"
}

Après 412, récupérez à nouveau la note, comparez son état actuel avec la modification souhaitée, puis envoyez un nouveau PATCH avec le nouvel ETag. Ne répétez pas indéfiniment la même requête avec l'ancienne valeur.


Notes - épingler et désépingler

Le champ pin est en lecture seule dans une mise à jour ordinaire. Utilisez la route dédiée pour définir le niveau d'épinglage :

curl --request POST --url "$BASE_URL/api/v1/notes/$NOTE_ID/pin" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-pin-20260905-0001" \
  --data-raw '{"pin":3}'

Les valeurs autorisées sont des entiers de 0 à 3. Pour désépingler la note, envoyez null :

curl --request POST --url "$BASE_URL/api/v1/notes/$NOTE_ID/pin" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-unpin-20260905-0001" \
  --data-raw '{"pin":null}'

L'opération nécessite notes:pin:write, un accès existant à la note et l'ETag actuel. Récupérez le nouvel ETag après la réussite. Ne définissez pas le pin via attributes.pin et n'envoyez pas de body vide.

Recherchez les notes épinglées avec un filtre :

curl --request GET --url "$BASE_URL/api/v1/notes?pin=3&page=1&pageSize=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Notes - relations disponibles avec les objets

Une note peut être liée aux cibles renvoyées par le schema. Le catalogue actuel comprend :

assets        - asset
clients       - client
vendors       - vendor
documents     - document
tickets       - ticket
changes       - change
problems      - problem
releases      - release
approvals     - approval
confirmations - confirmation
worktasks     - worktask
requesteditems - requesteditem

Une note ne peut pas être liée à elle-même. Pour la plupart des cibles, une relation se compose d'un identifiant, d'un ensemble et d'un type d'objet ; omettez donc relationshipType. Le modèle actuel des relations avec confirmations conserve ce paramètre. Exemple de cible de confirmation :

{
  "targetId": "6efaebb6-8650-4674-8478-34fd3e601427",
  "targetDataSet": "confirmations",
  "targetItemType": "confirmation",
  "relationshipType": "client"
}

L'API vérifie l'UUID, la concordance entre targetDataSet et targetItemType, l'existence et la visibilité de la cible, les autorisations et les doublons. Si le schéma ne renvoie pas une cible, ne l'utilisez pas dans l'intégration.


Notes - ajouter, lire et supprimer des relations

Lisez les relations via la collection :

curl --request GET --url "$BASE_URL/api/v1/notes/$NOTE_ID/relationships?targetDataSet=assets&targetItemType=asset&page=1&pageSize=50" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

L'ajout d'une relation avec un Asset exige notes:relationships:write, l'ETag actuel et une clé d'idempotence :

curl --request POST --url "$BASE_URL/api/v1/notes/$NOTE_ID/relationships" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-asset-relationship-20260905-0001" \
  --data-raw '{
    "targetId": "71ce005d-4cb8-4c9d-8239-ffe7e5e9f3e9",
    "targetDataSet": "assets",
    "targetItemType": "asset"
  }'

Un élément de la collection peut contenir targetId, targetDataSet, targetItemType, customId et name. Répéter le même ajout est sans danger et ne doit pas créer de doublon.

Supprimez une relation :

curl --request DELETE --url "$BASE_URL/api/v1/notes/$NOTE_ID/relationships/assets/71ce005d-4cb8-4c9d-8239-ffe7e5e9f3e9" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-asset-relationship-delete-20260905-0001"

Pour confirmations, ajoutez relationshipType=client à la query. Une réponse réussie a le statut 200 et data=true. Relisez la note après chaque changement de relation, car son ETag peut changer.


Notes - modification groupée des relations

Utilisez relationships:batch pour ajouter ou supprimer plusieurs relations. Une requête peut contenir les tableaux add et remove :

curl --request POST --url "$BASE_URL/api/v1/notes/$NOTE_ID/relationships:batch" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-relationships-batch-20260905-0001" \
  --data-raw '{
    "add": [
      {
        "targetId": "71ce005d-4cb8-4c9d-8239-ffe7e5e9f3e9",
        "targetDataSet": "assets",
        "targetItemType": "asset"
      }
    ],
    "remove": [
      {
        "targetId": "385b51cc-fb4d-4599-9b82-3c5b66705ccd",
        "targetDataSet": "clients",
        "targetItemType": "client"
      }
    ]
  }'

La réponse contient des compteurs :

{
  "data": {
    "added": 1,
    "removed": 1,
    "skipped": 0
  },
  "meta": {
    "requestId": "request-id-from-response"
  }
}

Chaque cible doit être visible et correspondre au catalogue des relations. Répéter une relation déjà existante peut être compté comme skipped. Des tableaux add et remove vides sont rejetés lorsqu'ils ne contiennent aucune opération. Un batch de relations modifie aussi l'ETag de la note source.


Notes - relation avec l'auteur

L'auteur est défini par le flux existant de création d'une note. Lisez-le via la relation utilisateur :

curl --request GET --url "$BASE_URL/api/v1/notes/$NOTE_ID/user-relationships?relationshipType=author&page=1&pageSize=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Exemple d'élément de réponse :

{
  "targetId": "8e9cbff3-340f-41f6-97ec-6997bb915829",
  "targetDataSet": "users",
  "relationshipType": "author",
  "displayName": "Fred Savage",
  "email": "[email protected]",
  "role": "Administrator"
}

La lecture exige notes:users:read et l'autorisation appropriée pour lister les notes. Le contrat actuel expose uniquement la relation author. Il n'existe pas de POST ni de DELETE public pour modifier ou supprimer l'auteur. N'envoyez pas l'auteur dans relationships ni dans attributes.


Notes - fichiers

Les notes peuvent contenir des fichiers, mais ne permettent pas de définir un fichier principal. Dans chaque ressource fichier, isMain vaut false. Les routes disponibles sont :

GET    /api/v1/notes/{NOTE_ID}/files
POST   /api/v1/notes/{NOTE_ID}/files
POST   /api/v1/notes/{NOTE_ID}/files/{FILE_ID}
DELETE /api/v1/notes/{NOTE_ID}/files/{FILE_ID}
GET    /api/v1/notes/{NOTE_ID}/files/{FILE_ID}/content

Commencez par vérifier la liste des fichiers :

curl --request GET --url "$BASE_URL/api/v1/notes/$NOTE_ID/files?page=1&pageSize=50" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Un élément de la liste contient notamment id, name, fileName, contentType, size, relationshipType, isMain et downloadUrl. La liste et le téléchargement exigent notes:files:read. L'envoi, l'association et la suppression exigent notes:files:write, les autorisations système, l'ETag actuel et une clé d'idempotence.

Envoyez un fichier au format multipart/form-data :

curl --request POST --url "$BASE_URL/api/v1/notes/$NOTE_ID/files?relationshipType=documentation" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-file-upload-20260905-0001" \
  --form "file=@./note-evidence.txt;type=text/plain"

Un envoi réussi renvoie 201 Created et l'identifiant du fichier. Le relationshipType peut décrire son usage, par exemple documentation, manual ou evidence. Lisez la limite de taille dans data.capabilities.limits.maxUploadBytes.

Téléchargez le contenu par le chemin authentifié :

curl --request GET --url "$BASE_URL/api/v1/notes/$NOTE_ID/files/$FILE_ID/content" \
  --header "Accept: application/octet-stream" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --output ./note-evidence.downloaded.txt

Traitez downloadUrl comme un chemin d'API, et non comme un lien public anonyme. L'endpoint content renvoie les octets du fichier, pas une enveloppe JSON.

Si le fichier existe déjà dans le stockage Codenica, associez son identifiant existant :

curl --request POST --url "$BASE_URL/api/v1/notes/$NOTE_ID/files/$FILE_ID?relationshipType=manual" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-file-attach-20260905-0001"

Supprimez la relation avec le fichier :

curl --request DELETE --url "$BASE_URL/api/v1/notes/$NOTE_ID/files/$FILE_ID" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-file-delete-20260905-0001"

Après chaque opération sur un fichier, relisez la note et enregistrez son nouvel ETag. Pour les Notes, n'appelez pas files/{FILE_ID}/main, car cette route ne fait pas partie du contrat de cet objet.


Notes - opérations batch

Le batch regroupe la création, la modification et la suppression de notes dans une seule requête. Chaque élément est traité séparément :

curl --request POST --url "$BASE_URL/api/v1/notes:batch" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: notes-batch-create-20260905-0001" \
  --data-raw '{
    "items": [
      {
        "operation": "create",
        "create": {
          "itemType": "note",
          "attributes": {
            "customId": "NOTE-BATCH-A",
            "title": "Batch note A",
            "description": "First note from a batch operation.",
            "category": "Integration",
            "status": "Open",
            "priority": "Normal",
            "isPrivate": false
          }
        }
      },
      {
        "operation": "create",
        "create": {
          "itemType": "note",
          "attributes": {
            "customId": "NOTE-BATCH-B",
            "title": "Batch note B",
            "description": "Second note from a batch operation.",
            "category": "Integration",
            "status": "Open",
            "priority": "Low",
            "isPrivate": true
          }
        }
      }
    ]
  }'

Un exemple de réponse contient succeeded, failed et le résultat de chaque élément :

{
  "data": {
    "succeeded": 2,
    "failed": 0,
    "items": [
      {
        "index": 0,
        "operation": "create",
        "status": 201,
        "id": "note-id-a"
      },
      {
        "index": 1,
        "operation": "create",
        "status": 201,
        "id": "note-id-b"
      }
    ]
  },
  "meta": {
    "requestId": "request-id-from-response"
  }
}

Avant un update ou un delete, récupérez séparément l'ETag actuel de chaque note. Dans un élément batch, envoyez id, ifMatch et le bloc update concerné. Utilisez operation=delete pour supprimer. Un batch n'est pas une transaction tout ou rien. En cas de réussite partielle, l'API peut renvoyer 207 Multi-Status ; contrôlez donc chaque élément.


Notes - supprimer un enregistrement

Avant la suppression, récupérez à nouveau la note et utilisez son ETag actuel :

curl --request DELETE --url "$BASE_URL/api/v1/notes/$NOTE_ID" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $NOTE_ETAG" \
  --header "Idempotency-Key: notes-delete-20260905-0001"

Une suppression réussie exige notes:delete et renvoie 200 OK avec data=true. Le flux de suppression existant nettoie aussi les relations selon la configuration du système.

Vérifiez l'enregistrement individuel après l'opération :

curl --request GET --url "$BASE_URL/api/v1/notes/$NOTE_ID" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Le statut attendu est 404 avec le code note_not_found. Vérifiez également votre identifiant personnalisé :

curl --request GET --url "$BASE_URL/api/v1/notes?customId=NOTE-ERP-2026-0001&page=1&pageSize=10" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Après une suppression réussie, totalItems doit être égal à 0. Retirez l'identifiant de l'index local de l'intégration ou marquez-le comme inactif.


Notes - erreurs, limites et sécurité

Les erreurs de l'API utilisent le format Problem Details avec des champs Codenica supplémentaires :

{
  "type": "https://docs.codenica.com/errors/note_not_found",
  "title": "Note not found.",
  "status": 404,
  "detail": "The note does not exist or is outside the caller's access scope.",
  "instance": "/api/v1/notes/{id}",
  "code": "note_not_found",
  "requestId": "request-id-from-response"
}

Dans la logique applicative, utilisez principalement status et code. Le texte de detail est une indication destinée à l'utilisateur et peut évoluer.

  • 400 - body, paramètre, UUID ou valeur de champ invalide ;
  • 401 - authentification absente ou invalide ;
  • 403 - scope ou autorisation utilisateur manquant ;
  • 404 - note, fichier, relation ou cible indisponible ;
  • 409 - conflit d'identifiant, doublon ou modification concurrente ;
  • 412 - ETag obsolète ;
  • 413 - fichier ou body trop volumineux ;
  • 422 - un flux métier existant a rejeté l'opération ;
  • 428 - If-Match ou Idempotency-Key manquant ;
  • 429 - limite de requêtes dépassée ;
  • 500 ou 503 - erreur serveur ou indisponibilité temporaire.

Lisez les en-têtes X-RateLimit-Limit, X-RateLimit-Remaining et, pour 429, Retry-After. Utilisez des répétitions contrôlées avec des délais croissants. Ne stockez jamais le Client Secret dans un dépôt, une URL, du code navigateur, l'historique du shell ou les journaux. isPrivate ne remplace pas le chiffrement.


Notes - séquence d'intégration

  1. Déterminez l'adresse réelle Cloud ou On-Premise et définissez BASE_URL.
  2. Créez une clé distincte pour l'application et l'environnement dans Paramètres - API - API Keys.
  3. Accordez uniquement les scopes nécessaires à la lecture, l'écriture, aux relations, aux fichiers, aux statistiques ou à l'épinglage.
  4. Envoyez GET /api/v1/context et vérifiez la base, le caller, les scopes et les limites.
  5. Récupérez GET /api/v1/notes/schema et construisez la correspondance des champs et des cibles de relations.
  6. Récupérez une liste de notes avec pagination, recherche ou filtres.
  7. Créez un enregistrement avec POST et un Idempotency-Key unique.
  8. Enregistrez l'UUID et l'ETag de la réponse.
  9. Après un timeout, répétez la requête identique avec la même clé d'idempotence.
  10. Récupérez un ETag récent avant chaque mutation.
  11. Utilisez PATCH pour les champs ordinaires et l'endpoint /pin pour l'épinglage.
  12. Utilisez uniquement les cibles de relations renvoyées par le schéma et le bon targetItemType.
  13. Pour confirmations, envoyez relationshipType=client ; omettez-le pour les autres ensembles.
  14. Lisez uniquement la relation de l'auteur, car aucun endpoint public d'écriture n'est disponible.
  15. N'oubliez pas que les Notes n'ont pas de fichier principal.
  16. Contrôlez chaque élément d'un batch, car une erreur partielle n'annule pas les éléments réussis.
  17. Après 412, récupérez à nouveau l'enregistrement et résolvez le conflit.
  18. Après 429, respectez Retry-After.
  19. Journalisez requestId, le statut et le code d'erreur, mais jamais le secret.
  20. À la fin de l'intégration, supprimez la clé API inutilisée.

Cette séquence permet de synchroniser les notes avec un autre système sans dépendre d'hypothèses sur les champs, la visibilité, les relations ou les données système.