Notas en Codenica API

Para trabajar con notas mediante Codenica API, empieza por crear una clave en los ajustes de Codenica. Si todavía no tienes una, abre en una pestaña nueva Codenica API - introducción. Allí se explican las reglas comunes para emitir claves, guardar el secreto y autenticar las solicitudes.

El nombre técnico del módulo es notes y el tipo de un objeto individual es note. Una nota es una entrada guardada en Codenica. Puede contener título, descripción, estado, prioridad, categoría, enlace y archivos. El campo isPrivate controla la visibilidad según los permisos existentes, mientras que pin establece el nivel de fijación de la entrada.

En las secciones siguientes se describen la dirección de la API, los scopes, el contexto, el esquema, los campos, las listas, los filtros, la creación, la idempotencia, el ETag, la edición, la fijación, las relaciones, el autor, los archivos, las operaciones por lotes y la eliminación.

Los ejemplos utilizan el prefijo PUBLIC-API-NOTE-20260905141812. Sustitúyelo por tu propio identificador y adapta las direcciones, los identificadores y los valores de los campos a los datos de tu base de datos.


Notas - dirección de la API y elección de la instalación

Todas las rutas de las notas empiezan por:

{BASE_URL}/api/v1/notes

BASE_URL es la dirección del servidor Codenica sin el sufijo final /api/v1. En Codenica Cloud, utiliza el dominio público asignado a la empresa correspondiente:

export BASE_URL="https://su-empresa.codenica.com"

En la instalación On-Premise predeterminada, la dirección registrada localmente por Codenica Discovery es:

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

Si el administrador ha publicado la instalación bajo un dominio corporativo, mediante un proxy inverso, con HTTPS o en otro puerto, utiliza la dirección exacta que te haya facilitado:

export BASE_URL="https://api.su-empresa.example"

No utilices localhost si el programa de integración se ejecuta en otro equipo distinto de la API. La base de datos utilizada se selecciona a partir de la dirección a la que se conecta la integración. No envíes tenantId en el body ni en la query string.


Notas - clave de API y límites de licencia

Crea una clave de API en Codenica, en Ajustes - API - API Keys. El secreto se muestra una sola vez, justo después de crear o rotar la clave. En ese momento, guarda el Client ID y el Client Secret en el almacenamiento seguro utilizado por la integración.

Codenica API está disponible con las licencias Plus y Enterprise. Plus permite crear hasta 50 claves activas y Enterprise hasta 100. Starter no incluye Codenica API. Crea una clave independiente para cada aplicación y entorno para poder gestionar sus scopes, rotar su secreto o retirar su acceso por separado.

Licencia
Acceso a la API
Número máximo de claves activas
Starter
No disponible
0
Plus
Disponible
50
Enterprise
Disponible
100

Al eliminar una clave, se elimina su registro y se libera una plaza del límite. Cuando pasa la fecha de caducidad, la clave deja de autenticar solicitudes, pero permanece en la lista hasta que se elimina. Si no estableces una fecha de finalización al crearla, la validez predeterminada es de 90 días. La validez máxima de una clave es de 5 años.


Notas - autenticación y solicitudes seguras

Autentica cada solicitud de Codenica API con las dos cabeceras de la clave:

export CLIENT_ID="cna_su_client_id"
export CLIENT_SECRET="cns_su_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"

Una integración externa no necesita el JWT del administrador ni las cookies del panel de Codenica. No guardes la clave en un repositorio, en código enviado al navegador, en una URL, en el historial del shell ni en los registros. Utiliza HTTPS fuera de las pruebas locales.

Conserva meta.requestId de la respuesta. Sirve para diagnosticar una solicitud concreta, pero no es el identificador de la nota y no debe tratarse como un secreto.


Notas - comprobar el contexto de conexión

Antes de realizar el primer guardado, lee el contexto. Así confirmarás que la dirección apunta a la base de datos correcta y que la clave elegida tiene los scopes necesarios:

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"

Comprueba en la respuesta:

  • data.apiVersion y data.contractVersion;
  • data.tenant.id, data.tenant.name y data.tenant.resolvedDomain;
  • data.caller.authentication con el valor api_key;
  • la presencia de notes en data.capabilities.resources;
  • los scopes asignados a la clave;
  • los límites de páginas, relaciones, archivos y solicitudes.

Si el contexto apunta a otra base de datos o no contiene un scope necesario, detén la integración y corrige la dirección o la clave. No se pueden añadir scopes a una solicitud individual.


Notas - scopes y permisos

La compatibilidad completa con las notas requiere los scopes correspondientes a las operaciones que utilizará tu integración:

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

Para las lecturas normales basta con notes:read. El esquema y las estadísticas utilizan notes:schema y notes:stats. Para crear, editar y eliminar, añade notes:write y notes:delete según corresponda.

  • notes:relationships:read y notes:relationships:write cubren las relaciones entre objetos;
  • notes:users:read permite leer el autor;
  • notes:files:read y notes:files:write cubren listar, descargar, subir, asociar y eliminar archivos;
  • notes:stats cubre las estadísticas y los valores de campos utilizados en filtros;
  • notes:pin:write es necesario para fijar y quitar la fijación;
  • utiliza los scopes técnicos solo si la integración necesita campos marcados como técnicos en el esquema o reglas de customValues.

Si la integración busca por sí misma los destinos de las relaciones, concede también los scopes de lectura correspondientes, por ejemplo assets:read, clients:read, vendors:read, documents:read, tickets:read, changes:read, problems:read, releases:read, approvals:read, confirmations:read, worktasks:read y requesteditems:read. Los scopes de la clave no sustituyen los permisos del usuario ni el acceso a una ubicación o departamento.


Notas - esquema y campos

El esquema indica qué campos se pueden leer y escribir en la base de datos seleccionada. Recupéralo antes de crear un formulario o un mapeo:

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 respuesta incluye, entre otros datos, data.itemType, data.fields y data.relationshipTargets. El valor fijo de itemType para este módulo es note. Para cada campo, comprueba readable, writable, required, technical, unique y maxLength. No construyas el mapeo únicamente a partir de los ejemplos de este artículo, porque la configuración puede variar entre bases de datos.

El esquema también informa de si están disponibles las relaciones con un conjunto determinado. Utiliza solo los destinos devueltos para la clave y el usuario actuales.


Notas - campos editables y campos del sistema

El catálogo público de campos de negocio de Notas incluye:

customId
location
department
isPrivate
tag
link
title
status
priority
category
description

Los límites principales de los campos son:

Campo
Tipo
Longitud máxima
customId
string
500
location, department
string
300 cada uno
isPrivate
boolean
-
tag, link
string
2000 cada uno
title
string
1000
status, priority, category
string
300 cada uno
description
string
10000

pin aparece en los atributos, pero no se puede cambiar mediante attributes. Para ello se utiliza su endpoint específico. Entre los campos del sistema y técnicos de solo lectura se encuentran:

id
itemType
pin
creator
updater
dateCreated
dateUpdated
importId
importSource
dateImported

id y itemType forman parte del recurso. El sistema asigna las fechas, el autor y el editor. No intentes cambiarlos mediante attributes.


Notas - endpoints principales

Las rutas principales del módulo notes son:

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

Todas las rutas requieren autenticación. Las operaciones que cambian datos también requieren Idempotency-Key, y las operaciones protegidas por versión requieren el If-Match actual. Comprueba los requisitos concretos en las respuestas de contexto y esquema.


Notas - listado y paginación

La lista está paginada. Este ejemplo obtiene la primera página y ordena las notas de la más reciente a la más antigua:

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 respuesta incluye, entre otros valores:

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

El pageSize máximo aparece en el contexto y normalmente es 100. Obtén las páginas siguientes mientras data.hasNextPage sea true. No des por hecho que el número de registros de la primera página es la lista completa.


Notas - búsqueda, filtros y ordenación

Puedes utilizar filtros de igualdad para campos como customId, location, department, isPrivate, tag, link, title, status, priority y category. Este ejemplo busca notas privadas abiertas del departamento de 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 busca en los campos de texto de la nota, como customId, tag, link, title, status, priority, category y 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"

El parámetro filter puede repetirse. Su formato es 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:

Entre los operadores disponibles están eq, ne, gt, gte, lt, lte, contains, startswith, endswith, empty y notempty. También existen los atajos =, !=, ge, le, sw y ew. Puedes añadir createdAfter, createdBefore, updatedAfter y updatedBefore. Ordena solo por un campo permitido por el esquema, usando direction=asc o direction=desc. Codifica en la URL los valores que contengan espacios o caracteres especiales.


Notas - selección de campos y datos incluidos

Si la integración solo necesita una parte de los datos, limita la respuesta con 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"

Puedes obtener una nota junto con sus archivos, relaciones e información sobre el autor:

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"

Los valores permitidos para include son files, relationships y users. Cada uno requiere su scope de lectura correspondiente. fields=* solicita todos los campos disponibles para la clave, pero los campos técnicos solo aparecen si se concede el scope técnico correspondiente.


Notas - estadísticas y valores de los campos

Las estadísticas cuentan las notas visibles y las agrupan por el campo seleccionado:

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"

Ejemplo de respuesta:

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

Sin el parámetro field, el endpoint devuelve el número total de notas. limit admite valores de 1 a 500. Los resultados respetan la visibilidad del usuario.

El endpoint values devuelve valores únicos que pueden utilizarse para crear listas de selección:

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"

Ejemplo de respuesta:

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

Ambos endpoints son de solo lectura y no modifican las notas. La respuesta de values no es una lista de registros, sino una lista de valores únicos de un campo concreto.


Notas - crear un registro

Incluye el tipo técnico note en el body y los campos de negocio en attributes. En una integración real conviene guardar un título y una descripción aunque el esquema no los marque como obligatorios:

{
  "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."
  }
}

Guarda el body como note-create.json y envíalo con una clave de idempotencia única:

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

Una creación correcta devuelve 201 Created. La respuesta contiene el UUID en data.id, data.itemType=note, los atributos guardados, las fechas del sistema y data.meta.etag. Deja que el sistema asigne id.

isPrivate controla la visibilidad, no es cifrado. No guardes contraseñas, tokens, Client Secret ni otros datos confidenciales en una nota.


Notas - repetir la creación de forma segura

Si el cliente no sabe si llegó la primera solicitud, repite exactamente la misma solicitud con la misma 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

Repetir la misma solicitud lógica no debe crear una segunda nota. La respuesta debería indicar el mismo UUID y el mismo resultado de la operación. No reutilices la clave para otro body, endpoint u operación. Cada nueva mutación necesita una nueva Idempotency-Key.

Después de un timeout no crees inmediatamente otro registro. Repite primero la solicitud anterior con el mismo body y la misma clave de idempotencia.


Notas - leer un registro y usar ETag

Después de crear una nota o antes de modificarla, obtén el registro y guarda su UUID y su ETag actual:

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"

El ETag aparece en la cabecera HTTP ETag, en data.meta.etag y en la envoltura meta.etag. Ejemplo de respuesta de un recurso:

{
  "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\""
  }
}

Después de cada mutación correcta, el ETag puede cambiar, también después de modificar una relación, un archivo o la fijación. Sustituye el valor anterior antes de realizar el siguiente cambio.


Notas - actualización parcial con If-Match

PATCH cambia solo los campos enviados en attributes. Utiliza el ETag actual y una clave de idempotencia diferente:

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
    }
  }'

No necesitas enviar todos los campos. Puedes borrar un valor opcional usando null si el esquema de la base de datos lo permite:

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

Un PATCH vacío, sin atributos, reglas de valores ni cambios de relaciones, se rechaza. Los campos del sistema y pin no forman parte de una actualización normal.


Notas - ETag obsoleto y ausencia de If-Match

Las mutaciones de Notas requieren la cabecera If-Match. Si falta, la API devuelve 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 el ETag enviado es antiguo, la API devuelve 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"
}

Después de un 412, vuelve a obtener la nota, compara su estado actual con el cambio que quieres realizar y solo entonces envía un nuevo PATCH con el nuevo ETag. No repitas indefinidamente la misma solicitud con el valor antiguo.


Notas - fijar y quitar la fijación

El campo pin es de solo lectura en una actualización normal. Utiliza la ruta específica para establecer el nivel de fijación:

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}'

Los valores permitidos son números enteros del 0 al 3. Para quitar la fijación, envía 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}'

La operación requiere notes:pin:write, acceso existente a la nota y el ETag actual. Después de realizarla, obtén el nuevo ETag. No establezcas el pin mediante attributes.pin ni envíes un body vacío.

Puedes buscar notas fijadas mediante un filtro:

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"

Notas - relaciones disponibles con objetos

Una nota puede vincularse con los destinos que devuelve el schema. El catálogo actual incluye:

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

Una nota no puede relacionarse consigo misma. Para la mayoría de los destinos, la relación se compone de identificador, conjunto y tipo de objeto, por lo que debes omitir relationshipType. El modelo actual de relación con confirmations conserva ese parámetro. Ejemplo de un destino de confirmación:

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

La API comprueba el UUID, la correspondencia entre targetDataSet y targetItemType, la existencia y visibilidad del destino, los permisos y los duplicados. Si el esquema no devuelve un destino, no lo utilices en la integración.


Notas - añadir, leer y eliminar relaciones

Lee las relaciones mediante la colección:

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"

Para añadir una relación con un Asset se necesitan notes:relationships:write, el ETag actual y una clave de idempotencia:

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 elemento de la colección puede contener targetId, targetDataSet, targetItemType, customId y name. Repetir el mismo alta es seguro y no debería crear un duplicado.

Para eliminar una relación:

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"

Para confirmations, añade relationshipType=client en la query. Una respuesta correcta tiene el estado 200 y data=true. Después de cada cambio de relación, vuelve a leer la nota porque su ETag puede cambiar.


Notas - cambio agrupado de relaciones

Utiliza relationships:batch para añadir o eliminar varias relaciones. Una solicitud puede contener los arrays add y 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 respuesta contiene contadores:

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

Cada destino debe ser visible y coincidir con el catálogo de relaciones. Repetir una relación ya existente puede contabilizarse como skipped. Los arrays add y remove vacíos se rechazan si no contienen ninguna operación. Un batch de relaciones también cambia el ETag de la nota de origen.


Notas - relación con el autor

El autor se asigna mediante el flujo existente de creación de notas. Puedes leerlo a través de la relación de usuarios:

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"

Ejemplo de un elemento de respuesta:

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

La lectura requiere notes:users:read y el permiso correspondiente para listar notas. El contrato actual solo expone la relación author. No existe un POST ni un DELETE público para cambiar o eliminar el autor. No envíes el autor en relationships ni en attributes.


Notas - archivos

Las notas pueden tener archivos, pero no permiten establecer un archivo principal. En cada recurso de archivo, isMain es false. Las rutas disponibles son:

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

Empieza comprobando la lista de archivos:

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 elemento de la lista incluye, entre otros, id, name, fileName, contentType, size, relationshipType, isMain y downloadUrl. Listar y descargar requiere notes:files:read. Subir, asociar y eliminar requiere notes:files:write, permisos del sistema, el ETag actual y una clave de idempotencia.

Envía un archivo como 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"

Una carga correcta devuelve 201 Created y el identificador del archivo. relationshipType puede indicar su finalidad, por ejemplo documentation, manual o evidence. Lee el límite de tamaño en data.capabilities.limits.maxUploadBytes.

Descarga el contenido mediante la ruta autenticada:

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

Trata downloadUrl como una ruta de la API, no como un enlace público anónimo. El endpoint content devuelve los bytes del archivo, no una envoltura JSON.

Si el archivo ya existe en el almacenamiento de Codenica, puedes asociar su identificador existente:

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"

Elimina la relación con el archivo:

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"

Después de cada operación con archivos, vuelve a leer la nota y guarda el nuevo ETag. Para Notas no llames a files/{FILE_ID}/main, porque esa ruta no forma parte del contrato de este objeto.


Notas - operaciones por lotes

Batch permite combinar la creación, actualización y eliminación de notas en una solicitud. Cada elemento se procesa por separado:

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
          }
        }
      }
    ]
  }'

Una respuesta de ejemplo contiene succeeded, failed y el resultado de cada elemento:

{
  "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"
  }
}

Antes de un update o delete, obtén por separado el ETag actual de cada nota. En cada elemento batch envía id, ifMatch y el bloque update correspondiente. Para eliminar, utiliza operation=delete. Un batch no es una transacción de todo o nada. Si el resultado es parcial, la API puede devolver 207 Multi-Status, por lo que debes comprobar cada elemento.


Notas - eliminar un registro

Antes de eliminarlo, vuelve a obtener la nota y utiliza su ETag actual:

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"

La eliminación correcta requiere notes:delete y devuelve 200 OK con data=true. El flujo de eliminación existente también limpia las relaciones según la configuración del sistema.

Comprueba el registro individual después de la operación:

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"

El estado esperado es 404 con el código note_not_found. Comprueba también tu identificador personalizado:

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"

Después de eliminar correctamente, totalItems debería ser 0. Elimina el identificador del índice local de la integración o márcalo como inactivo.


Notas - errores, límites y seguridad

Los errores de la API utilizan el formato Problem Details con campos adicionales de Codenica:

{
  "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"
}

En la lógica de la aplicación, utiliza sobre todo status y code. El texto de detail es una indicación para las personas y puede cambiar.

  • 400 - body, parámetro, UUID o valor de campo no válido;
  • 401 - autenticación ausente o no válida;
  • 403 - falta un scope o un permiso del usuario;
  • 404 - nota, archivo, relación o destino no disponible;
  • 409 - conflicto de identificador, duplicado o cambio simultáneo;
  • 412 - ETag obsoleto;
  • 413 - el archivo o el body supera el límite;
  • 422 - un flujo de dominio existente rechazó la operación;
  • 428 - falta If-Match o Idempotency-Key;
  • 429 - se superó el límite de solicitudes;
  • 500 o 503 - error del servidor o indisponibilidad temporal.

Lee las cabeceras X-RateLimit-Limit, X-RateLimit-Remaining y, para 429, Retry-After. Utiliza reintentos controlados con retrasos crecientes. Nunca guardes Client Secret en un repositorio, una URL, código del navegador, el historial del shell ni los registros. isPrivate no sustituye al cifrado.


Notas - secuencia de integración

  1. Determina la dirección real de Cloud o On-Premise y establece BASE_URL.
  2. Crea una clave independiente para la aplicación y el entorno en Ajustes - API - API Keys.
  3. Concede solo los scopes necesarios para leer, escribir, gestionar relaciones, archivos, estadísticas o fijación.
  4. Envía GET /api/v1/context y comprueba la base de datos, el caller, los scopes y los límites.
  5. Obtén GET /api/v1/notes/schema y crea el mapeo de campos y destinos de relaciones.
  6. Obtén una lista de notas con paginación, búsqueda o filtros.
  7. Crea un registro mediante POST con una Idempotency-Key única.
  8. Guarda el UUID y el ETag de la respuesta.
  9. Después de un timeout, repite la solicitud idéntica con la misma clave de idempotencia.
  10. Obtén un ETag nuevo antes de cada mutación.
  11. Utiliza PATCH para los campos normales y el endpoint /pin para fijar.
  12. Utiliza solo los destinos de relaciones devueltos por el esquema y el targetItemType correcto.
  13. Para confirmations, envía relationshipType=client; omítelo para los demás conjuntos.
  14. Lee únicamente la relación del autor, porque no existe un endpoint público de escritura.
  15. Recuerda que Notas no tiene archivo principal.
  16. Comprueba cada elemento de un batch, porque un error parcial no revierte los elementos correctos.
  17. Después de 412, vuelve a obtener el registro y resuelve el conflicto.
  18. Después de 429, respeta Retry-After.
  19. Registra requestId, el estado y el código de error, pero nunca el secreto.
  20. Cuando termine la integración, elimina la clave de API que ya no utilices.

Esta secuencia permite sincronizar notas con otro sistema sin depender de suposiciones sobre campos, visibilidad, relaciones o datos del sistema.