Clients and employees in Codenica API

The technical name of this resource in the Public API is clients, while the type returned by the API is client. This collection lets you work with client and employee data, depending on the role, type and information stored in your database. Requests use one resource, and the distinction comes from the record field values.

Before sending your first request, prepare the key described in Codenica API - introduction. The rest of this article presents the complete workflow: checking the schema and listing records, creating and updating them, then managing relationships, files, batch operations and record deletion.

  • reading client and employee lists with pagination, sorting and filters;
  • reading only the fields required by the integration;
  • creating records and applying partial updates to contact or organisational data;
  • protecting changes with ETag and If-Match;
  • retrying operations safely with Idempotency-Key;
  • linking records with assets, documents, tickets and other supported objects;
  • uploading, downloading, attaching and deleting files;
  • reading statistics, field values and using batch operations.

Required fields and available values may depend on your database configuration. Read the current schema for the data type you are working with before writing data.


Clients and employees - API address and installation choice

Send requests to the public address at which your Codenica installation is available. Do not use the address of the database itself, a container or a port available only inside the server. Client and employee paths start with:

{BASE_URL}/api/v1/clients

In Codenica Cloud, use the domain assigned to your installation:

export BASE_URL="https://your-company.codenica.com"

In the default On-Premise installation, the address registered locally by Codenica Discovery is:

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

If an administrator has published the On-Premise installation through a company domain, reverse proxy, HTTPS or another external port, use the exact address provided for that installation:

export BASE_URL="https://api.your-company.example"

The correct database is selected from the host address. Do not try to select it with tenantId, an additional query-string field or a value in the body. Do not use localhost when the integrating program runs on a different computer from the API. In production, use HTTPS when the installation is published with a certificate.

BASE_URL should not contain the final /api/v1:

# Codenica Cloud:
export BASE_URL="https://your-company.codenica.com"

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

# On-Premise with a custom domain or reverse proxy:
# export BASE_URL="https://api.your-company.example"

Clients and employees - API key and access scopes

Create the key for an external integration in Codenica under Settings - API - API Keys. Give it a name that describes the application, environment and purpose, for example CRM production - Clients. Then select only the scopes required by that integration and save the displayed Client ID and Client Secret once in a secure secret store.

The complete client and employee workflow in this article requires the following scopes:

  • clients:read, clients:write and clients:delete - reading, creating, updating and deleting records;
  • clients:schema - the field schema and relationship targets;
  • clients:stats - statistics and field values;
  • clients:relationships:read and clients:relationships:write - reading and changing relationships;
  • clients:files:read and clients:files:write - file operations.

If the integration links records to another object, it also needs a read scope for that target, for example assets:read for existing assets. The client relationship scope does not replace permission to read the target object.

For a read-only integration, these scopes are usually enough:

clients:read
clients:schema

The active-key limits depend on the licence:

Licence
Public API
Active keys
Starter
unavailable
0
Plus
available
50
Enterprise
available
100

The API panel shows created keys and lets you rotate or delete them. A deleted key can no longer authenticate requests and is not counted as active. The Client Secret is displayed only when the key is created or rotated. Do not save it in a repository, URL, logs, command history or code running in the browser.


Clients and employees - authentication headers

The external application sends server-to-server requests with two headers:

export CLIENT_ID="cna_your_client_id"
export CLIENT_SECRET="cns_your_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"

Do not use the administrator's Bearer JWT or a panel session in the integration. The JWT is used to log a user in to Codenica, while the API key connects an external application to the selected database. Use HTTPS outside a test environment.

Requests that change data also require a unique header:

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

After reading a record, include its current ETag in a request that changes data:

If-Match: "current-client-etag"

Do not generate a new idempotency key when retrying the same request. The same key and identical body let you safely reproduce the result of an operation that may have ended with a timeout.


Clients and employees - checking the installation context

Before starting synchronisation, read the context:

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"

Check apiVersion, contractVersion, the database identifier, tenant.resolvedDomain, caller.authentication equal to api_key, the required scopes and clients in capabilities.resources. Also read the page, upload and request limits.

In the completed test flow, the context confirmed, among other things, clients:read, clients:write, clients:delete, clients:schema, clients:stats, relationship and file scopes, as well as support for batch operations, relationships, files, ETags and idempotency.

Keep meta.requestId. If the context points to the wrong installation or a scope is missing, stop synchronisation and correct the address or key. Do not try to change the database in the request body.


Clients and employees - field schema and data types

The schema shows which fields can be read and written and which values are accepted in your database:

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"

In the response, data.itemType has the value client. In the current schema, the following fields are required, and the e-mail address is unique:

Field
Type
Required
Unique
Example meaning
firstName
string
yes
no
first name or first part of a name
lastName
string
yes
no
last name or second part of a name
email
string
yes
yes
contact address

Frequently used optional fields include:

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

Before using an additional field, check its readable, writable, type and length limit in the schema. Do not assume that status, type, category or role values are identical in every installation. Creating a Client does not require the technical itemType in the body - the API returns it as client.

The schema also confirms these relationship targets: assets, documents, tickets, notes, worktasks, confirmations and requesteditems. In the current schema, clients is not a Client-to-Client relationship target.


Clients and employees - endpoint map

The following map covers the main operations. Replace the values in braces with identifiers received from the API responses.

  • GET /api/v1/clients - list;
  • GET /api/v1/clients/schema - field and relationship schema;
  • GET /api/v1/clients/stats - statistics;
  • GET /api/v1/clients/values - field values;
  • GET /api/v1/clients/{id} - single record;
  • POST /api/v1/clients - create;
  • PATCH /api/v1/clients/{id} - partial update;
  • DELETE /api/v1/clients/{id} - delete;
  • POST /api/v1/clients:batch - create, update and delete operations;
  • GET /api/v1/clients/{id}/relationships - relationship list;
  • POST /api/v1/clients/{id}/relationships - add a relationship;
  • POST /api/v1/clients/{id}/relationships:batch - change relationships in one request;
  • DELETE /api/v1/clients/{id}/relationships/{targetDataSet}/{targetId} - remove a relationship;
  • GET /api/v1/clients/{id}/files - file list;
  • POST /api/v1/clients/{id}/files - upload;
  • POST /api/v1/clients/{id}/files/{fileId} - attach an existing file;
  • PUT /api/v1/clients/{id}/files/{fileId}/main - set the primary file;
  • DELETE /api/v1/clients/{id}/files/{fileId} - delete or detach a file;
  • GET /api/v1/clients/{id}/files/{fileId}/content - download file content.

A 403 response usually means that the key is missing a scope or that the user assigned to the key does not have the required permission.


Clients and employees - listing and pagination

Read the list page by page. This example returns the first twenty records:

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"

The collection response contains items, page, pageSize, totalItems, totalPages and hasNextPage. Continue while hasNextPage is true. If order matters for synchronisation, always set sorting explicitly.

Read the pageSize limit from the context. Do not assume that the first page contains every record or that the default order will remain unchanged.


Clients and employees - searching and filtering

The test example finds a record by its own identifier, status and data type:

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"

Depending on the schema, you can use parameters such as ids, search, firstName, lastName, displayName, email, status, category, type, role, location, department, customId, tag, createdAfter, createdBefore, updatedAfter and updatedBefore.

Use filter for precise conditions:

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

The operators include eq, ne, in, contains, startsWith, endsWith, empty, notEmpty, gt, gte, lt and lte. URL-encode values containing spaces or special characters according to URL rules.


Clients and employees - selecting fields and including data

The fields parameter limits the response to the fields you need:

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"

Use include to read files and relationships together with the record:

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"

In the test, the response contained the requested fields and the files and relationships collections. Read access to included data must be granted separately. The absence of clients:files:read or clients:relationships:read cannot be bypassed with fields=*.


Clients and employees - creating a record

Use POST /api/v1/clients to create a record. Put writable fields inside attributes. The following example shows a complete client or employee profile received from a CRM system:

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": "en",
      "phone": "+1 600 000 001",
      "department": "Customer Service",
      "description": "Source client used by the complete Public API Clients flow."
    }
  }'

This resource does not require the technical itemType in the body. The API returns itemType: client itself. In the tested schema, firstName, lastName and a unique email were required. Your database may require additional fields or different values.

A successful response has status 201 Created. Save data.id, the ETag from the HTTP header and data.meta.etag. customId makes it easier to find the record later in the external system.


Clients and employees - safely retrying creation

If a timeout occurs after sending the data and you do not know whether the record was saved, send exactly the same request with the same Idempotency-Key and identical body:

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": "en",
      "phone": "+1 600 000 001",
      "department": "Customer Service",
      "description": "Source client used by the complete Public API Clients flow."
    }
  }'

In the completed test, the second identical request returned the same record identifier and ETag. No second Client was created. Changing the body or using the same key for a different operation is not a retry - create a new key for a new operation.


Clients and employees - reading and partially updating a record

Keep the record UUID after creation. Read a single profile as follows:

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 changes only the fields you send. This example updates the display name and description:

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

A successful update returns 200 OK and a new ETag. Replace the old ETag with the new one after every change. Relationship and file operations can also change the record version, so read the current ETag again before the next mutation.


Clients and employees - protection against overwriting changes

If another operation changes the record after the integration has read its ETag, the old If-Match value is rejected:

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

After this error, do not overwrite the record blindly. Read the Client again, compare the changes and only then prepare a new PATCH with the current ETag. Omitting If-Match for an operation that requires version control returns:

HTTP/1.1 428 Precondition Required

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

Clients and employees - available relationship targets

The current schema lists these target data sets:

  • assets - assets, for example computer;
  • documents - the document type returned by the schema;
  • tickets - the ticket type returned by the schema;
  • notes - the note type returned by the schema;
  • worktasks - the task type returned by the schema;
  • confirmations - the confirmation type returned by the schema;
  • requesteditems - the requested-item type returned by the schema.

targetItemType must match the actual type of the target. In the completed test, two existing assets of type computer were selected dynamically. If the relationship points to assets, the key must also have assets:read. Use the appropriate read scope for other targets.

In the current schema, clients is not a Client-to-Client relationship target. Create relationships only with objects listed in the current schema response.


Clients and employees - adding and reading relationships

This example links the record to an existing asset. The relationship body contains the target identifier, data set, type and relationship kind:

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

A successful add returns 201 Created and target details, including targetId, targetDataSet, targetItemType, relationshipType, customId and name. Read the relationship collection as follows:

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"

The response uses the same pagination model as the client list. After one relationship was added in the test, totalItems was 1.


Clients and employees - batch relationships and removing one link

Use relationships:batch to make several changes in one operation:

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

The response provides added, removed and skipped counters. Read the Client's new ETag after the batch.

Remove one relationship with:

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"

Success returns 200 OK with data: true. After the last relationship is removed, the collection should return totalItems: 0.


Clients and employees - file list and upload

Files are handled separately from record fields. A new Client initially has an empty collection:

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"

Send the file as multipart/form-data. The following example creates a primary documentation file:

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"

The response includes, among other fields, id, fileName, contentType, size, relationshipType, isMain and a relative downloadUrl. The test file clients-primary.txt was 62 bytes. After uploading, check the file list because it shows the final isMain state.


Clients and employees - downloading a file and changing the primary file

Download the file content through the content endpoint. Use binary output:

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

You can upload a second file with 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"

To make it the primary file:

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"

The operation returns data: true. Afterwards, the first file has isMain: false and the second has isMain: true. The record ETag changes, so read it again before the next mutation.


Clients and employees - attaching an existing file

If a file is already stored with one Client, you can attach it to another record without uploading it again:

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"

Verify isMain in the target Client's file list, not only in the direct attach response. Detach it with:

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"

Detach removes the attachment from the target Client but does not delete the file from the owner Client.


Clients and employees - deleting a file

Before deleting a file, read a fresh file list and the record ETag. If you delete the current primary file, the system may automatically select another file as primary:

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"

After a 200 response, update the ETag and check the list. Deleting the last file does not delete the client or employee record; it leaves an empty file collection. If the file was only attached to the record, remove the attachment and only then consider deleting the file where it was stored.


Clients and employees - statistics and field values

Statistics show the distribution of data, while the values endpoint returns values useful for building filters:

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"

In the test, the statistics included statuses such as aktywny, Active, w magazynie and Urlop płatny. A mixed result is possible when data comes from different sources - do not assume that statuses will be only in English or only in one language.

Example values response:

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

Statistics and values do not change data. Use values to build filters and suggestions instead of hard-coding dictionaries in the integration.


Clients and employees - batch creation

Batch lets you create several records in one request. A create item contains operation: create and a create object with 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

In the completed test, the response had 200 OK, succeeded: 2, failed: 0 and two items with operation status 201. Save every new UUID and ETag separately.


Clients and employees - batch updates and partial success

An update requires id, the current ifMatch and an update object. The following example also includes an invalid item to explain a partial response:

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

In the test, the response was 207 Multi-Status:

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

207 does not mean total failure. Check the result of every operation separately, and remember a separate ifMatch for a delete operation:

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

Clients and employees - deleting a record

Deleting a profile is irreversible from the API. First read the current ETag:

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"

Then send 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"

Success returns 200 OK and data: true. A later read returns 404 Not Found with the client_not_found code. Filtering by the prefix PUBLIC-API-CLI-20260905104704 should return totalItems: 0.


Clients and employees - errors, limits and security

Errors are returned in the Problem Details format. The most important fields are status, code, detail and requestId. Base application logic on the stable code field.

  • 400 - invalid fields, target type or batch item;
  • 401 - missing or invalid credentials;
  • 403 - missing scope or permission;
  • 404 - record, file or target does not exist or is not visible;
  • 409 - data conflict, such as an e-mail address that is already in use;
  • 412 - stale ETag;
  • 413 - file exceeds the limit;
  • 422 - domain validation error;
  • 428 - missing If-Match or Idempotency-Key;
  • 429 - request limit exceeded;
  • 503 - service temporarily unavailable;
  • 207 - batch completed partially.

Read X-RateLimit-Limit and X-RateLimit-Remaining. On 429, use Retry-After when returned and increase the delay between subsequent attempts. Do not log X-Codenica-Client-Secret, secrets or sensitive file contents.


Clients and employees - complete integration workflow

  1. Set BASE_URL to the actual Codenica Cloud or On-Premise address.
  2. Create a key under Settings - API - API Keys, select the minimum scopes and save the secret in a credential store.
  3. Read /api/v1/context and confirm the correct database, caller, scopes and limits.
  4. Read /api/v1/clients/schema and inspect required fields, values and relationship targets.
  5. Send a filtered GET with a unique customId to rule out a duplicate.
  6. Create the client or employee record with a unique Idempotency-Key.
  7. Save the UUID and ETag from the response. If the response is lost, repeat the identical create request with the same key.
  8. Read the profile with fields and optional include=files,relationships.
  9. Change fields with PATCH, the current If-Match and a new idempotency key.
  10. Add, read and remove only relationships allowed by the schema. Check the target's targetItemType.
  11. Handle files through the dedicated endpoints, keeping the current ETag and distinguishing an attachment from file deletion.
  12. After every upload, attach, primary-file change and DELETE, check the file list.
  13. Use stats and values to synchronise filters and dictionaries.
  14. For larger sets, use clients:batch and handle both 200 and 207.
  15. Before deletion, read the ETag, send DELETE and confirm the client_not_found code.

The examples in this article come from a flow with the prefix PUBLIC-API-CLI-20260905104704. Your integration must use identifiers received from your database, not demonstration values.

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