Clientes y empleados en Codenica API

El nombre técnico de este recurso en la API pública es clients y el tipo que devuelve la API es client. Esta colección permite gestionar datos de clientes y empleados, según el rol, el tipo y la información guardada en tu base de datos. Las solicitudes utilizan un único recurso y la diferencia se obtiene de los valores de los campos del registro.

Antes de enviar la primera solicitud, prepara la clave descrita en Codenica API - introducción. En las secciones siguientes se muestra el flujo completo: comprobar el esquema y las listas, crear y actualizar registros y, después, gestionar relaciones, archivos, operaciones por lotes y la eliminación del registro.

  • leer listas de clientes y empleados con paginación, ordenación y filtros;
  • leer solo los campos que necesita la integración;
  • crear registros y aplicar actualizaciones parciales a los datos de contacto u organización;
  • proteger los cambios con ETag y If-Match;
  • repetir solicitudes de forma segura mediante Idempotency-Key;
  • relacionar registros con activos, documentos, tickets y otros objetos compatibles;
  • subir, descargar, adjuntar y eliminar archivos;
  • consultar estadísticas, valores de campos y operaciones por lotes.

Los campos obligatorios y los valores disponibles pueden depender de la configuración de tu base de datos. Lee el esquema actual del tipo de datos que vas a utilizar antes de escribir datos.


Clientes y empleados - dirección de la API y elección de la instalación

Envía las solicitudes a la dirección pública donde está disponible tu instalación de Codenica. No utilices la dirección de la propia base de datos, de un contenedor ni de un puerto accesible únicamente dentro del servidor. Las rutas de clientes y empleados comienzan por:

{BASE_URL}/api/v1/clients

En Codenica Cloud, utiliza el dominio asignado a tu instalación:

export BASE_URL="https://tu-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 On-Premise mediante un dominio de la empresa, un proxy inverso, HTTPS u otro puerto externo, utiliza la dirección exacta indicada para esa instalación:

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

La base de datos correcta se selecciona a partir de la dirección del host. No intentes seleccionarla mediante tenantId, un campo adicional en la query string ni un valor en el body. No utilices localhost si el programa de integración se ejecuta en otro ordenador distinto de la API. En producción, utiliza HTTPS cuando la instalación esté publicada con un certificado.

BASE_URL no debe incluir el /api/v1 final:

# Codenica Cloud:
export BASE_URL="https://tu-empresa.codenica.com"

# On-Premise predeterminado con Codenica Discovery:
# export BASE_URL="http://codenica.local:5150"

# On-Premise con dominio propio o proxy inverso:
# export BASE_URL="https://api.tu-empresa.example"

Clientes y empleados - clave API y permisos de acceso

Crea la clave para la integración externa en Codenica, en Settings - API - API Keys. Asígnale un nombre que describa la aplicación, el entorno y el propósito, por ejemplo CRM producción - Clientes. Después selecciona únicamente los scopes necesarios para esa integración y guarda una sola vez el Client ID y el Client Secret mostrados, en un almacén seguro de secretos.

El flujo completo de clientes y empleados de este artículo requiere los siguientes scopes:

  • clients:read, clients:write y clients:delete - leer, crear, actualizar y eliminar registros;
  • clients:schema - esquema de campos y destinos de relaciones;
  • clients:stats - estadísticas y valores de campos;
  • clients:relationships:read y clients:relationships:write - leer y modificar relaciones;
  • clients:files:read y clients:files:write - gestionar archivos.

Si la integración relaciona registros con otro objeto, también necesita el scope de lectura de ese destino, por ejemplo assets:read para activos existentes. El scope de relaciones de clientes no sustituye el permiso para leer el objeto de destino.

Para una integración de solo lectura, normalmente bastan estos scopes:

clients:read
clients:schema

Los límites de claves activas dependen de la licencia:

Licencia
API pública
Claves activas
Starter
no disponible
0
Plus
disponible
50
Enterprise
disponible
100

El panel de API muestra las claves creadas y permite rotarlas o eliminarlas. Una clave eliminada ya no puede autenticar solicitudes y no se cuenta como activa. El Client Secret solo se muestra al crear o rotar la clave. No lo guardes en un repositorio, una URL, los registros, el historial de comandos ni en código que se ejecute en el navegador.


Clientes y empleados - encabezados de autenticación

La aplicación externa envía solicitudes de servidor a servidor mediante dos encabezados:

export CLIENT_ID="cna_tu_client_id"
export CLIENT_SECRET="cns_tu_client_secret"

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

No utilices en la integración el JWT Bearer del administrador ni una sesión del panel. El JWT sirve para iniciar sesión en Codenica, mientras que la clave API conecta una aplicación externa con la base de datos seleccionada. Utiliza HTTPS fuera de un entorno de pruebas.

Las solicitudes que modifican datos también requieren un encabezado único:

Idempotency-Key: public-api-clients-create-20260905104704

Después de leer un registro, incluye su ETag actual en cualquier solicitud que modifique datos:

If-Match: "etag-cliente-actual"

No generes una nueva clave de idempotencia al repetir la misma solicitud. La misma clave y un body idéntico permiten recuperar de forma segura el resultado de una operación que pudo terminar con un timeout.


Clientes y empleados - comprobar el contexto de la instalación

Antes de iniciar la sincronización, lee el contexto:

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 apiVersion, contractVersion, el identificador de la base de datos, tenant.resolvedDomain, caller.authentication igual a api_key, los scopes necesarios y clients dentro de capabilities.resources. Lee también los límites de páginas, subidas y solicitudes.

En el flujo de pruebas completado, el contexto confirmó, entre otros, clients:read, clients:write, clients:delete, clients:schema, clients:stats, los scopes de relaciones y archivos y la compatibilidad con operaciones por lotes, relaciones, archivos, ETags e idempotencia.

Conserva meta.requestId. Si el contexto apunta a una instalación incorrecta o falta un scope, detén la sincronización y corrige la dirección o la clave. No intentes cambiar de base de datos en el body de la solicitud.


Clientes y empleados - esquema de campos y tipos de datos

El esquema muestra qué campos se pueden leer y escribir y qué valores se aceptan en tu base de datos:

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

En la respuesta, data.itemType tiene el valor client. En el esquema actual, los siguientes campos son obligatorios y la dirección de correo electrónico es única:

Campo
Tipo
Obligatorio
Único
Significado de ejemplo
firstName
string
no
nombre o primera parte del nombre
lastName
string
no
apellido o segunda parte del nombre
email
string
dirección de contacto

Entre los campos opcionales más utilizados están:

customId, displayName, gender, position, category, contractType,
type, role, status, phone, phoneWork, phoneMobile, address, country,
city, state, zipCode, location, department, section, roomNumber, tag,
link, number, value, isLicensed, isVerified, comments, description,
notification, preferredLanguage

Antes de utilizar un campo adicional, comprueba en el esquema sus propiedades readable, writable, el tipo y el límite de longitud. No des por hecho que los valores de status, type, category o role son idénticos en todas las instalaciones. Para crear un Cliente no es necesario incluir el itemType técnico en el body: la API devuelve client.

El esquema también confirma estos destinos de relaciones: assets, documents, tickets, notes, worktasks, confirmations y requesteditems. En el esquema actual, clients no aparece como destino de una relación Cliente-Cliente.


Clientes y empleados - mapa de endpoints

El siguiente mapa incluye las operaciones principales. Sustituye los valores entre llaves por los identificadores recibidos en las respuestas de la API.

  • GET /api/v1/clients - lista;
  • GET /api/v1/clients/schema - esquema de campos y relaciones;
  • GET /api/v1/clients/stats - estadísticas;
  • GET /api/v1/clients/values - valores de campos;
  • GET /api/v1/clients/{id} - registro individual;
  • POST /api/v1/clients - creación;
  • PATCH /api/v1/clients/{id} - actualización parcial;
  • DELETE /api/v1/clients/{id} - eliminación;
  • POST /api/v1/clients:batch - operaciones de creación, actualización y eliminación;
  • GET /api/v1/clients/{id}/relationships - lista de relaciones;
  • POST /api/v1/clients/{id}/relationships - añadir una relación;
  • POST /api/v1/clients/{id}/relationships:batch - cambiar relaciones en una sola solicitud;
  • DELETE /api/v1/clients/{id}/relationships/{targetDataSet}/{targetId} - eliminar una relación;
  • GET /api/v1/clients/{id}/files - lista de archivos;
  • POST /api/v1/clients/{id}/files - subida;
  • POST /api/v1/clients/{id}/files/{fileId} - adjuntar un archivo existente;
  • PUT /api/v1/clients/{id}/files/{fileId}/main - establecer el archivo principal;
  • DELETE /api/v1/clients/{id}/files/{fileId} - eliminar o desvincular un archivo;
  • GET /api/v1/clients/{id}/files/{fileId}/content - descargar el contenido del archivo.

Una respuesta 403 suele indicar que falta un scope en la clave o que el usuario asociado a ella no tiene el permiso necesario.


Clientes y empleados - listas y paginación

Lee la lista por páginas. Este ejemplo devuelve los primeros veinte registros:

curl --request GET --url "$BASE_URL/api/v1/clients?page=1&pageSize=20&sort=displayName&direction=asc" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

La respuesta de la colección contiene items, page, pageSize, totalItems, totalPages y hasNextPage. Continúa mientras hasNextPage sea true. Si el orden importa para la sincronización, establece siempre la ordenación de forma explícita.

Lee el límite de pageSize en el contexto. No supongas que la primera página contiene todos los registros ni que el orden predeterminado se mantendrá sin cambios.


Clientes y empleados - búsqueda y filtrado

El ejemplo de la prueba busca un registro por su identificador propio, su estado y su tipo de datos:

curl --request GET --url "$BASE_URL/api/v1/clients?customId=PUBLIC-API-CLI-20260905104704-SOURCE&status=Active&sort=customId&direction=asc&page=1&pageSize=10" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Según el esquema, puedes utilizar parámetros como ids, search, firstName, lastName, displayName, email, status, category, type, role, location, department, customId, tag, createdAfter, createdBefore, updatedAfter y updatedBefore.

Para condiciones precisas, utiliza filter:

filter=status:eq:Active
filter=displayName:contains:Public
filter=category:in:Customer,Employee
filter=description:notEmpty:

Los operadores incluyen eq, ne, in, contains, startsWith, endsWith, empty, notEmpty, gt, gte, lt y lte. Codifica según las reglas de las URL los valores que contengan espacios o caracteres especiales.


Clientes y empleados - seleccionar campos e incluir datos

El parámetro fields limita la respuesta a los campos necesarios:

curl --request GET --url "$BASE_URL/api/v1/clients?fields=id,itemType,customId,displayName,email,status,department" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Utiliza include para leer archivos y relaciones junto con el registro:

curl --request GET --url "$BASE_URL/api/v1/clients/$CLIENT_RECORD_ID?fields=customId,displayName,email,status,description&include=files,relationships" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

En la prueba, la respuesta contenía los campos solicitados y las colecciones files y relationships. El acceso de lectura a los datos incluidos debe concederse por separado. La ausencia de clients:files:read o clients:relationships:read no se puede evitar utilizando fields=*.


Clientes y empleados - crear un registro

Utiliza POST /api/v1/clients para crear un registro. Coloca los campos que se pueden escribir dentro de attributes. El ejemplo siguiente muestra un perfil completo de cliente o empleado recibido desde un sistema CRM:

export IDEMPOTENCY_KEY="public-api-clients-create-source-20260905104704"

curl --request POST --url "$BASE_URL/api/v1/clients" \
  --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: $IDEMPOTENCY_KEY" \
  --data-raw '{
    "attributes": {
      "customId": "PUBLIC-API-CLI-20260905104704-SOURCE",
      "firstName": "Public API",
      "lastName": "Client source 20260905104704",
      "displayName": "Public API client source 20260905104704",
      "email": "[email protected]",
      "category": "Customer",
      "type": "External",
      "role": "Customer",
      "status": "Active",
      "preferredLanguage": "es",
      "phone": "+34 600 000 001",
      "department": "Customer Service",
      "description": "Source client used by the complete Public API Clients flow."
    }
  }'

Este recurso no requiere el itemType técnico en el body. La API devuelve por sí misma itemType: client. En el esquema probado eran obligatorios firstName, lastName y un email único. Tu base de datos puede requerir campos adicionales o valores diferentes.

Una respuesta correcta tiene el estado 201 Created. Guarda data.id, el ETag del encabezado HTTP y data.meta.etag. customId facilita encontrar más adelante el registro en el sistema externo.


Clientes y empleados - repetir la creación de forma segura

Si se produce un timeout después de enviar los datos y no sabes si el registro se ha guardado, envía exactamente la misma solicitud con la misma Idempotency-Key y un body idéntico:

curl --request POST --url "$BASE_URL/api/v1/clients" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: public-api-clients-create-source-20260905104704" \
  --data-raw '{
    "attributes": {
      "customId": "PUBLIC-API-CLI-20260905104704-SOURCE",
      "firstName": "Public API",
      "lastName": "Client source 20260905104704",
      "displayName": "Public API client source 20260905104704",
      "email": "[email protected]",
      "category": "Customer",
      "type": "External",
      "role": "Customer",
      "status": "Active",
      "preferredLanguage": "es",
      "phone": "+34 600 000 001",
      "department": "Customer Service",
      "description": "Source client used by the complete Public API Clients flow."
    }
  }'

En la prueba completada, la segunda solicitud idéntica devolvió el mismo identificador de registro y el mismo ETag. No se creó un segundo Cliente. Cambiar el body o utilizar la misma clave para otra operación no es repetir una solicitud: crea una clave nueva para una operación nueva.


Clientes y empleados - leer y actualizar parcialmente un registro

Conserva el UUID del registro después de crearlo. Para leer un perfil individual:

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

PATCH cambia únicamente los campos que envías. Este ejemplo actualiza el nombre visible y la descripción:

curl --request PATCH --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-update-source-20260905104704" \
  --data-raw '{
    "attributes": {
      "displayName": "Public API client source updated",
      "description": "Updated through the Codenica Public API Clients flow."
    }
  }'

Una actualización correcta devuelve 200 OK y un ETag nuevo. Sustituye el ETag antiguo por el nuevo después de cada cambio. Las operaciones de relaciones y archivos también pueden cambiar la versión del registro, por lo que debes volver a leer el ETag actual antes de la siguiente mutación.


Clientes y empleados - protección frente a la sobrescritura de cambios

Si otra operación modifica el registro después de que la integración haya leído su ETag, el valor antiguo de If-Match se rechaza:

HTTP/1.1 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 client version.",
  "instance": "/api/v1/clients/{clientId}",
  "code": "if_match_failed",
  "requestId": "request-id-from-response"
}

Después de este error, no sobrescribas el registro sin comprobarlo. Vuelve a leer el Cliente, compara los cambios y solo entonces prepara un PATCH nuevo con el ETag actual. Omitir If-Match en una operación que requiere control de versiones devuelve:

HTTP/1.1 428 Precondition Required

{
  "code": "if_match_required",
  "status": 428,
  "detail": "Send the ETag returned by GET in the If-Match header."
}

Clientes y empleados - destinos de relaciones disponibles

El esquema actual indica los siguientes conjuntos de datos de destino:

  • assets - activos, por ejemplo computer;
  • documents - el tipo de documento que devuelve el esquema;
  • tickets - el tipo de ticket que devuelve el esquema;
  • notes - el tipo de nota que devuelve el esquema;
  • worktasks - el tipo de tarea que devuelve el esquema;
  • confirmations - el tipo de confirmación que devuelve el esquema;
  • requesteditems - el tipo de solicitud que devuelve el esquema.

targetItemType debe coincidir con el tipo real del destino. En la prueba completada se seleccionaron dinámicamente dos activos existentes de tipo computer. Si la relación apunta a activos, la clave también debe tener assets:read. Para los demás destinos, utiliza el scope de lectura correspondiente.

En el esquema actual, clients no aparece como destino de una relación Cliente-Cliente. Crea relaciones únicamente con los objetos incluidos en la respuesta actual del esquema.


Clientes y empleados - añadir y leer relaciones

Este ejemplo relaciona el registro con un activo existente. El body de la relación contiene el identificador del destino, su conjunto de datos, su tipo y el tipo de relación:

{
  "targetId": "635d6518-1ac0-496a-abb7-95636b1b19b9",
  "targetDataSet": "assets",
  "targetItemType": "computer",
  "relationshipType": "related"
}
curl --request POST --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/relationships" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-relationship-add-20260905104704" \
  --data @client-relationship.json

Una adición correcta devuelve 201 Created y los datos del destino, incluidos targetId, targetDataSet, targetItemType, relationshipType, customId y name. Lee la colección de relaciones así:

curl --request GET --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/relationships?targetDataSet=assets&targetItemType=computer&relationshipType=related&page=1&pageSize=50" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

La respuesta utiliza el mismo modelo de paginación que la lista de clientes. Después de añadir una relación en la prueba, totalItems tenía el valor 1.


Clientes y empleados - relaciones por lotes y eliminación de un vínculo

Utiliza relationships:batch para realizar varios cambios en una sola operación:

{
  "add": [
    {
      "targetId": "5ddc5b5a-5bdc-46f9-ab7e-5ad8f6775b3a",
      "targetDataSet": "assets",
      "targetItemType": "computer",
      "relationshipType": "related"
    }
  ],
  "remove": []
}
curl --request POST --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/relationships:batch" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-relationship-batch-20260905104704" \
  --data @client-relationship-batch.json

La respuesta contiene los contadores added, removed y skipped. Después del lote, vuelve a leer el ETag del Cliente.

Elimina una relación individual con:

curl --request DELETE --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/relationships/assets/635d6518-1ac0-496a-abb7-95636b1b19b9?relationshipType=related" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-relationship-remove-20260905104704"

Si tiene éxito, devuelve 200 OK con data: true. Después de eliminar la última relación, la colección debe devolver totalItems: 0.


Clientes y empleados - lista de archivos y subida

Los archivos se gestionan por separado de los campos del registro. Un Cliente nuevo comienza con una colección vacía:

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

Envía el archivo como multipart/form-data. El ejemplo siguiente crea un archivo principal de documentación:

curl --request POST --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/files?makeMain=true&relationshipType=documentation" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-file-upload-primary-20260905104704" \
  --form "[email protected];type=text/plain"

La respuesta incluye, entre otros campos, id, fileName, contentType, size, relationshipType, isMain y un downloadUrl relativo. El archivo de prueba clients-primary.txt tenía 62 bytes. Después de subirlo, comprueba la lista de archivos porque muestra el estado final de isMain.


Clientes y empleados - descargar un archivo y cambiar el archivo principal

Descarga el contenido mediante el endpoint content. Utiliza una salida binaria:

curl --request GET --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/files/$FILE_ID/content" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --output clients-primary.downloaded.txt

Puedes subir un segundo archivo con makeMain=false:

curl --request POST --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/files?makeMain=false&relationshipType=manual" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-file-upload-secondary-20260905104704" \
  --form "[email protected];type=text/plain"

Para convertirlo en el archivo principal:

curl --request PUT --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/files/$SECONDARY_FILE_ID/main" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-file-set-main-20260905104704"

La operación devuelve data: true. Después, el primer archivo tiene isMain: false y el segundo isMain: true. El ETag del registro cambia, así que debes volver a leerlo antes de la siguiente mutación.


Clientes y empleados - adjuntar un archivo existente

Si un archivo ya está guardado con un Cliente, puedes adjuntarlo a otro registro sin volver a subirlo:

owner client: 8844622a-f948-4f2a-a718-81f61fa5ab21
target client: 3569dead-82b1-439e-8ecd-e4ee6b5f886b
file: cd60b8ce-1a97-47a9-b9fc-b1abe8b4dee4
curl --request POST --url "$BASE_URL/api/v1/clients/3569dead-82b1-439e-8ecd-e4ee6b5f886b/files/cd60b8ce-1a97-47a9-b9fc-b1abe8b4dee4?makeMain=true&relationshipType=manual" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-file-attach-existing-20260905104704"

Verifica isMain en la lista de archivos del Cliente de destino, no solo en la respuesta directa del adjunto. Desvincúlalo con:

curl --request DELETE --url "$BASE_URL/api/v1/clients/3569dead-82b1-439e-8ecd-e4ee6b5f886b/files/cd60b8ce-1a97-47a9-b9fc-b1abe8b4dee4" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-file-detach-20260905104704"

Desvincularlo lo elimina del Cliente de destino, pero no borra el archivo del Cliente propietario.


Clientes y empleados - eliminar un archivo

Antes de eliminar un archivo, lee una lista actualizada y el ETag del registro. Si eliminas el archivo principal actual, el sistema puede seleccionar automáticamente otro archivo como principal:

curl --request DELETE --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE/files/$FILE_ID" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-file-delete-20260905104704"

Después de una respuesta 200, actualiza el ETag y comprueba la lista. Eliminar el último archivo no elimina el registro del cliente o del empleado: deja una colección de archivos vacía. Si el archivo solo estaba adjunto al registro, elimina el vínculo y solo después considera borrar el archivo en el lugar donde estaba guardado.


Clientes y empleados - estadísticas y valores de campos

Las estadísticas muestran la distribución de los datos, mientras que el endpoint values devuelve valores útiles para crear filtros:

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

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

En la prueba, las estadísticas incluyeron estados como aktywny, Active, w magazynie y Urlop płatny. Es posible obtener una mezcla cuando los datos proceden de distintas fuentes: no supongas que los estados estarán únicamente en español o en un solo idioma.

Ejemplo de respuesta de values:

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

Las estadísticas y los valores no modifican los datos. Utiliza values para crear filtros y sugerencias en lugar de incluir diccionarios fijos en el código de la integración.


Clientes y empleados - creación por lotes

Las operaciones por lotes permiten crear varios registros en una sola solicitud. Un elemento de creación contiene operation: create y un objeto create con attributes:

{
  "items": [
    {
      "operation": "create",
      "create": {
        "attributes": {
          "customId": "PUBLIC-API-CLI-20260905104704-BATCH-A",
          "firstName": "Public API",
          "lastName": "Clients batch A 20260905104704",
          "displayName": "Public API clients batch A 20260905104704",
          "email": "[email protected]",
          "category": "Customer",
          "role": "Customer",
          "status": "Active",
          "description": "Client created by the Public API batch flow."
        }
      }
    },
    {
      "operation": "create",
      "create": {
        "attributes": {
          "customId": "PUBLIC-API-CLI-20260905104704-BATCH-B",
          "firstName": "Public API",
          "lastName": "Clients batch B 20260905104704",
          "displayName": "Public API clients batch B 20260905104704",
          "email": "[email protected]",
          "category": "Employee",
          "role": "Employee",
          "status": "Active"
        }
      }
    }
  ]
}
curl --request POST --url "$BASE_URL/api/v1/clients:batch" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: public-api-clients-batch-create-20260905104704" \
  --data @clients-batch-create.json

En la prueba completada, la respuesta tuvo 200 OK, succeeded: 2, failed: 0 y dos elementos con estado de operación 201. Guarda por separado cada UUID y cada ETag nuevos.


Clientes y empleados - actualización por lotes y éxito parcial

Una actualización requiere id, el ifMatch actual y un objeto update. El ejemplo siguiente incluye también un elemento no válido para explicar una respuesta parcial:

{
  "items": [
    {
      "operation": "update",
      "id": "942f8323-bb7b-4915-80a9-81eaae657cb8",
      "ifMatch": "\"3drgMRaLiRYP6p6nCd6JDh_UHVRhYIAwWFDO4YLOUvc\"",
      "update": {
        "attributes": {
          "displayName": "Public API client batch A updated",
          "description": "Updated inside a partial Clients batch."
        }
      }
    },
    {
      "operation": "invalid"
    }
  ]
}
curl --request POST --url "$BASE_URL/api/v1/clients:batch" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: public-api-clients-batch-partial-20260905104704" \
  --data @clients-batch-partial.json

En la prueba, la respuesta fue 207 Multi-Status:

{
  "data": {
    "succeeded": 1,
    "failed": 1,
    "items": [
      {"operation": "update", "status": 200},
      {
        "operation": "invalid",
        "status": 400,
        "error": {"code": "invalid_batch_item"}
      }
    ]
  }
}

207 no significa un fallo total. Comprueba el resultado de cada operación por separado y recuerda utilizar un ifMatch independiente para una operación delete:

{
  "operation": "delete",
  "id": "4db45845-ddec-4740-bb4b-f3c57836d3b5",
  "ifMatch": "\"NAHxuB2XecVkevWAbNdCeT6aQkwyyYHc_TDtgLvfI_U\""
}

Clientes y empleados - eliminar un registro

Eliminar un perfil es irreversible desde la API. Primero lee el ETag actual:

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

Después envía DELETE:

curl --request DELETE --url "$BASE_URL/api/v1/clients/$CLIENT_ID_VALUE" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-clients-delete-source-20260905104704"

Si tiene éxito, devuelve 200 OK y data: true. Una lectura posterior devuelve 404 Not Found con el código client_not_found. El filtrado por el prefijo PUBLIC-API-CLI-20260905104704 debe devolver totalItems: 0.


Clientes y empleados - errores, límites y seguridad

Los errores se devuelven en formato Problem Details. Los campos principales son status, code, detail y requestId. Basa la lógica de la aplicación en el campo estable code.

  • 400 - campos, tipo de destino o elemento de lote no válidos;
  • 401 - credenciales ausentes o no válidas;
  • 403 - falta un scope o un permiso;
  • 404 - el registro, el archivo o el destino no existe o no es visible;
  • 409 - conflicto de datos, por ejemplo una dirección de correo única ya utilizada;
  • 412 - ETag obsoleto;
  • 413 - el archivo supera el límite;
  • 422 - error de validación de negocio;
  • 428 - falta If-Match o Idempotency-Key;
  • 429 - se ha superado el límite de solicitudes;
  • 503 - el servicio no está disponible temporalmente;
  • 207 - el lote se ha completado parcialmente.

Lee X-RateLimit-Limit y X-RateLimit-Remaining. En caso de 429, utiliza Retry-After cuando se devuelva y aumenta el tiempo de espera entre intentos sucesivos. No registres X-Codenica-Client-Secret, secretos ni contenido sensible de archivos.


Clientes y empleados - flujo completo de integración

  1. Establece BASE_URL con la dirección real de Codenica Cloud u On-Premise.
  2. Crea una clave en Settings - API - API Keys, selecciona los scopes mínimos y guarda el secreto en un almacén de credenciales.
  3. Lee /api/v1/context y confirma la base de datos correcta, el caller, los scopes y los límites.
  4. Lee /api/v1/clients/schema y comprueba los campos obligatorios, los valores y los destinos de relaciones.
  5. Envía un GET filtrado con un customId único para descartar un duplicado.
  6. Crea el registro del cliente o empleado con una Idempotency-Key única.
  7. Guarda el UUID y el ETag de la respuesta. Si se pierde la respuesta, repite la creación idéntica con la misma clave.
  8. Lee el perfil con fields y, de forma opcional, include=files,relationships.
  9. Cambia los campos mediante PATCH, el If-Match actual y una nueva clave de idempotencia.
  10. Añade, lee y elimina únicamente las relaciones permitidas por el esquema. Comprueba el targetItemType del destino.
  11. Gestiona los archivos mediante los endpoints específicos, conservando el ETag actual y diferenciando entre adjuntar y eliminar un archivo.
  12. Después de cada subida, adjunto, cambio del archivo principal y DELETE, comprueba la lista de archivos.
  13. Utiliza stats y values para sincronizar filtros y diccionarios.
  14. Para conjuntos grandes, utiliza clients:batch y gestiona tanto 200 como 207.
  15. Antes de eliminar, lee el ETag, envía DELETE y confirma el código client_not_found.

Los ejemplos de este artículo proceden de un flujo con el prefijo PUBLIC-API-CLI-20260905104704. Tu integración debe utilizar identificadores recibidos de tu base de datos, no valores de demostración.

context = GET /api/v1/context
schema = GET /api/v1/clients/schema

client = POST /api/v1/clients
  Idempotency-Key: unique-create-key

client = GET /api/v1/clients/{id}
etag = client.data.meta.etag

updated = PATCH /api/v1/clients/{id}
  If-Match: etag
  Idempotency-Key: unique-update-key

relationship = POST /api/v1/clients/{id}/relationships
  If-Match: updated-etag
  Idempotency-Key: unique-relationship-key

files = GET /api/v1/clients/{id}/files

deleted = DELETE /api/v1/clients/{id}
  If-Match: latest-etag
  Idempotency-Key: unique-delete-key