Documents in Codenica API
In the Public API, the technical name of this object is documents. A document can represent an invoice, purchase order, contract, protocol or another document stored in your database. The examples use an invoice record with invoice data sent from an external system.
Before sending your first request, create the key described in Codenica API - introduction. The sections below cover the complete document workflow: checking the schema and listing records, creating and updating them, handling relationships and files, using batch operations and deleting a record.
- reading document lists with pagination, sorting and filters;
- reading invoice data and other document types;
- creating records and applying partial updates;
- protecting changes with ETag and
If-Match; - retrying operations safely with
Idempotency-Key; - linking documents with one another and with other objects;
- uploading, downloading, attaching and deleting files;
- reading statistics, field values and processing batch operations.
Required fields and available values can depend on your database configuration. Read the current schema for the document type you are working with before writing data.
Documents - API address and deployment choice
Send requests to the public address where your Codenica installation is available. Do not use the database address, a container address or a port that can be reached only from inside the server. Document paths begin with:
{BASE_URL}/api/v1/documentsFor Codenica Cloud, use the public 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 the 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 choose it through tenantId, an extra query-string field or a value in the body. Do not use localhost when the integrating application runs on a different computer from the API. In production, use HTTPS when the installation is published with a certificate.
Documents - API key scopes
Create the API key in Codenica under Settings - API - API Keys. Give it a name that identifies the application and environment, then select only the scopes required for document operations.
The complete workflow in this article requires:
documents:read- listing and reading documents;documents:write- creating and updating;documents:delete- deleting documents;documents:schema- reading fields and relationship targets;documents:stats- statistics and field values;documents:relationships:readanddocuments:relationships:write- reading and changing relationships;documents:files:readanddocuments:files:write- file operations.
For a read-only integration, documents:read and documents:schema are usually enough. Add statistics, relationship and file scopes only when the integration needs them.
Technical fields may require documents:technical:read, while writing secret fields requires documents:secrets:write. After creation, save the Client ID and Client Secret in a secure store. The secret is shown only during creation or rotation.
Documents - 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/documents" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"Do not use an administrator Bearer JWT or a panel session in the integration. A JWT signs a user into Codenica; an API key connects an external application to the selected database. Use HTTPS outside a local test environment.
Do not store the secret in a repository, URL, logs, command history or code delivered to a browser. The examples use placeholder values.
Documents - checking the installation context
Read the context before performing the actual operations:
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 documents in capabilities.resources. Also read the page, upload and request limits.
Keep meta.requestId. If the context points to the wrong installation or a scope is missing, stop synchronization and correct the address or key. Do not try to change the database through the request body.
Documents - field and type schema
The schema shows which fields can be read and written and which values your database accepts:
curl --request GET --url "$BASE_URL/api/v1/documents/schema" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"For itemType=invoice, check the required fields first:
datedateTimedocNumberstringThe schema also describes readable, writable, required, technical, secretWriteOnly, options, maximum length, uniqueness and automatic-generation rules. The field set is not necessarily the same in every database.
The key and keyType fields are secret fields. They are not returned in ordinary responses and cannot be used for filtering, sorting, statistics or field-value queries. Compare the request body with the schema before sending it.
Documents - available endpoints
The map below covers the main operations. Replace the values in braces with identifiers from API responses.
GET /api/v1/documents- list;GET /api/v1/documents/schema- field and relationship schema;GET /api/v1/documents/stats- statistics;GET /api/v1/documents/values- field values;GET /api/v1/documents/{id}- individual document;POST /api/v1/documents- create;PATCH /api/v1/documents/{id}- partial update;DELETE /api/v1/documents/{id}- delete;POST /api/v1/documents:batch- create, update and delete operations;GET /api/v1/documents/{id}/relationships- relationship list;POST /api/v1/documents/{id}/relationships- add a relationship;POST /api/v1/documents/{id}/relationships:batch- group relationship changes;DELETE /api/v1/documents/{id}/relationships/{targetDataSet}/{targetId}- remove a relationship;GET /api/v1/documents/{id}/files- file list;POST /api/v1/documents/{id}/files- upload;POST /api/v1/documents/{id}/files/{fileId}- attach an existing file;PUT /api/v1/documents/{id}/files/{fileId}/main- set the main file;DELETE /api/v1/documents/{id}/files/{fileId}- delete or detach a file;GET /api/v1/documents/{id}/files/{fileId}/content- download content.
A 403 response usually means that the key lacks a scope or that the user assigned to it does not have the required permission.
Documents - listing and pagination
Read the list page by page. This example returns the first ten invoice documents:
curl --request GET --url "$BASE_URL/api/v1/documents?itemType=invoice&page=1&pageSize=10&sort=date&direction=desc" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"A collection response contains items, page, pageSize, totalItems, totalPages and hasNextPage. Continue while hasNextPage is true. If ordering matters to synchronization, always set an explicit sort order.
Read the pageSize limit from the context. Do not assume that the first page contains every invoice or that the default order will remain unchanged.
Documents - searching and filtering
The test example finds a document by its custom identifier, type and status:
curl --request GET --url "$BASE_URL/api/v1/documents?itemType=invoice&customId=PUBLIC-API-DOC-20260905101715-SOURCE&status=Draft&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, use parameters such as itemType, ids, search, customId, docNumber, name, status, category, currency, createdAfter, createdBefore, updatedAfter and updatedBefore.
For precise conditions, use filter:
filter=status:eq:Draft
filter=docNumber:contains:2026
filter=category:in:Procurement,Sales
filter=description:notEmpty:Operators include eq, ne, in, contains, startsWith, endsWith, empty, notEmpty, gt, gte, lt and lte. Encode values containing spaces or special characters according to URL rules.
Documents - selecting fields and including data
The fields parameter limits the response to the fields you need:
curl --request GET --url "$BASE_URL/api/v1/documents?itemType=invoice&fields=id,itemType,customId,docNumber,name,status,total" \
--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/documents/$DOCUMENT_ID?include=files,relationships" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"Access to included data must be granted separately. Missing documents:files:read or documents:relationships:read cannot be bypassed with fields=*. Technical and secret fields are returned only when the scopes and schema allow them.
Documents - creating a record
Use POST /api/v1/documents to create a document. Put the type in itemType and writable fields in attributes. This example represents an invoice sent from an accounting system:
export IDEMPOTENCY_KEY="documents-create-20260905-0001"
curl --request POST --url "$BASE_URL/api/v1/documents" \
--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 '{
"itemType": "invoice",
"attributes": {
"customId": "PUBLIC-API-DOC-20260905101715-SOURCE",
"date": "2026-09-05T10:17:15Z",
"docNumber": "FV/2026/0001",
"name": "Invoice from ERP",
"category": "Procurement",
"type": "invoice",
"status": "Draft",
"currency": "PLN",
"paymentMethod": "bank_transfer",
"total": 1250.50,
"description": "Document imported from the external accounting system."
}
}'In the tested invoice schema, date and docNumber were required. Your database can require additional fields or different values. Do not send read-only fields or an id unless the schema explicitly allows it.
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 document later in the external system.
Documents - safely retrying creation
If a timeout occurs after sending an invoice, you do not yet know whether the record was saved. Send exactly the same request with the same Idempotency-Key and identical body. Do not create a new key merely because the first response did not arrive:
curl --request POST --url "$BASE_URL/api/v1/documents" \
--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 '{
"itemType": "invoice",
"attributes": {
"customId": "PUBLIC-API-DOC-20260905101715-SOURCE",
"date": "2026-09-05T10:17:15Z",
"docNumber": "FV/2026/0001",
"name": "Invoice from ERP",
"category": "Procurement",
"type": "invoice",
"status": "Draft",
"currency": "PLN",
"paymentMethod": "bank_transfer",
"total": 1250.50,
"description": "Document imported from the external accounting system."
}
}'An idempotent retry returns the same document instead of creating a duplicate. The same key must not later describe a different body, endpoint or intention. Such reuse returns 422 idempotency_key_reused. Use a new value for every new intention.
Documents - reading one record
After creating or finding a document, read it by UUID:
export DOCUMENT_ID="11111111-1111-1111-1111-111111111111"
curl --request GET --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID?include=files,relationships" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"The response contains id, itemType, fields in attributes and metadata in meta. You will find the current ETag in the HTTP header and usually also in data.meta.etag and the envelope meta.etag.
Read a fresh ETag before every change to a document, relationship or file. Do not use a value saved earlier if another person or integration may have changed the record.
Documents - partial update with ETag
PATCH changes only the fields sent in the body. It requires the current If-Match value and a new Idempotency-Key:
export CURRENT_ETAG='"etag-v1"'
export UPDATE_IDEMPOTENCY_KEY="documents-update-20260905-0001"
curl --request PATCH --url "$BASE_URL/api/v1/documents/$DOCUMENT_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: $CURRENT_ETAG" \
--header "Idempotency-Key: $UPDATE_IDEMPOTENCY_KEY" \
--data-raw '{
"attributes": {
"status": "Approved",
"total": 1350.75,
"description": "Invoice approved after verification in the accounting system."
}
}'You do not have to send the entire document. Fields omitted from the body remain unchanged. Save the new ETag returned by the API after a successful operation.
Documents - protection against an outdated change
The API rejects a change without the current ETag. Omitting If-Match returns 428 if_match_required:
curl --request PATCH --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID" \
--header "Content-Type: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "Idempotency-Key: documents-update-without-etag-0001" \
--data-raw '{"attributes":{"status":"Approved"}}'If you send an old ETag, the API returns 412 if_match_failed and leaves the record unchanged. Read the document again, inspect the new version and only then decide whether to send your change again.
{
"status": 412,
"code": "if_match_failed",
"detail": "The supplied ETag is not the current document version.",
"requestId": "request-id-from-response"
}The same rule applies to document deletion, relationship changes and file operations whenever the path changes the record.
Documents - relationships and valid targets
A document can be linked to another object when the target is visible to the key and allowed by the schema. Check relationshipTargets in the schema response before sending a relationship.
Send targetId, targetDataSet, optional targetItemType and relationshipType when the selected target supports it. This example directly links two invoices:
curl --request POST --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/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: documents-relationship-add-0001" \
--data-raw '{
"targetId": "22222222-2222-2222-2222-222222222222",
"targetDataSet": "documents",
"targetItemType": "invoice",
"relationshipType": "related"
}'Do not send a targetItemType that differs from the target's actual type. Do not create a relationship to the same record or to a record that is not visible to the key.
Documents - reading and removing relationships
Read the current relationship list separately or together with the document:
curl --request GET --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/relationships?targetDataSet=documents&targetItemType=invoice&relationshipType=related&page=1&pageSize=50" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"To remove one relationship, read a fresh document ETag and send:
curl --request DELETE --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/relationships/documents/$TARGET_DOCUMENT_ID?relationshipType=related&targetItemType=invoice" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "If-Match: $CURRENT_ETAG" \
--header "Idempotency-Key: documents-relationship-delete-0001"A successful removal returns 200 with data=true. Read the list again afterwards and save the document's new ETag.
Documents - changing several relationships at once
Use relationships:batch to add and remove several relationships in one request:
curl --request POST --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/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: documents-relationship-batch-0001" \
--data-raw '{
"add": [
{
"targetId": "33333333-3333-3333-3333-333333333333",
"targetDataSet": "documents",
"targetItemType": "invoice",
"relationshipType": "related"
}
],
"remove": [
{
"targetId": "22222222-2222-2222-2222-222222222222",
"targetDataSet": "documents",
"targetItemType": "invoice",
"relationshipType": "related"
}
]
}'The response reports the added, removed and skipped counts. Use a current ETag even when the batch contains only one change. If the result is partial, inspect every item before sending another request.
Documents - listing and uploading a file
Files are handled separately from document fields. Start by reading the current list:
curl --request GET --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/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. This example creates a text file and makes it the main file:
printf 'Invoice attachment created by the ERP integration.\n' > invoice-primary.txt
export FILE_UPLOAD_ETAG='"etag-v1"'
curl --request POST --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/files?makeMain=true&relationshipType=documentation" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "If-Match: $FILE_UPLOAD_ETAG" \
--header "Idempotency-Key: documents-file-upload-0001" \
--form "[email protected];type=text/plain"The response contains id, fileName, contentType, size, relationshipType, isMain and downloadUrl. This URL is relative to BASE_URL.
Documents - downloading a file and changing the main file
Download the file content through the content endpoint. Save it as binary data:
curl --request GET --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/files/$FILE_ID/content" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--output downloaded-invoice-fileYou can upload a second file with makeMain=false. To change the main file, read the current document ETag and call:
curl --request PUT --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/files/$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: documents-file-main-0001"Read the file list after the change. Exactly one file should have isMain=true. Save the new ETag after the operation.
Documents - attaching an existing file
If a file is already stored with another document, attach it to another record without uploading its content again:
export TARGET_DOCUMENT_ID="11111111-1111-1111-1111-111111111111"
export EXISTING_FILE_ID="44444444-4444-4444-4444-444444444444"
curl --request POST --url "$BASE_URL/api/v1/documents/$TARGET_DOCUMENT_ID/files/$EXISTING_FILE_ID?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: documents-file-attach-0001"Attaching creates a document-to-file relationship. Detaching with DELETE /documents/{id}/files/{fileId} removes the relationship from that document but does not delete a file belonging to another document. Deleting the file from its owner document is a separate operation.
Documents - deleting a file
Read a fresh document file list and ETag before deleting a file. If you delete the current main file, the system can automatically select another file as the main one:
curl --request DELETE --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID/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: documents-file-delete-0001"After a 200 response, update the ETag and read the list again. Deleting the last file does not delete the document; it leaves an empty file collection. If the file was only attached to the document, remove the relationship first and only then consider deleting the file where it was stored.
Documents - 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/documents/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/documents/values?field=status&search=Draf&limit=20" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"Example values response:
{
"data": {
"field": "status",
"values": ["Draft", "Approved", "Paid"]
},
"meta": {
"requestId": "request-id-from-response"
}
}Statistics and values do not change data. Do not use them for secret or technical fields without the appropriate scope.
Documents - batch operations
The documents:batch endpoint can create, update and delete several documents in one request. update and delete operations require their own ETag for each item:
curl --request POST --url "$BASE_URL/api/v1/documents:batch" \
--header "Content-Type: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "Idempotency-Key: documents-batch-0001" \
--data-raw '{
"items": [
{
"operation": "create",
"create": {
"itemType": "invoice",
"attributes": {
"customId": "PUBLIC-API-DOC-20260905101715-TARGET-A",
"date": "2026-09-05T10:18:00Z",
"docNumber": "FV/2026/0002",
"name": "Related invoice",
"category": "Procurement",
"type": "invoice",
"status": "Draft",
"currency": "PLN",
"paymentMethod": "bank_transfer",
"total": 510.00
}
}
},
{
"operation": "update",
"id": "11111111-1111-1111-1111-111111111111",
"ifMatch": "\"etag-v1\"",
"update": {
"attributes": {
"status": "Approved"
}
}
},
{
"operation": "delete",
"id": "22222222-2222-2222-2222-222222222222",
"ifMatch": "\"etag-v3\""
}
]
}'With complete success, the response is 200; with partial success, it is 207. Batch is not an all-or-nothing transaction. Save the identifiers, ETags and statuses of each operation.
Documents - deleting a record
Deleting a document cannot be undone through the API. Read the current ETag and check that the UUID and database address are correct:
curl --request DELETE --url "$BASE_URL/api/v1/documents/$DOCUMENT_ID" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "If-Match: $CURRENT_ETAG" \
--header "Idempotency-Key: documents-delete-0001"A successful response returns 200 and data=true. A later read of the document returns 404 document_not_found. If the record has relationships or files, save any data you need outside the system before deleting it.
Documents - errors, limits and security
Errors use 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, document type or relationship;401- missing or invalid credentials;403- missing scope or permission;404- document or target does not exist or is not visible;409- data conflict;412- outdated ETag;413- file exceeds the limit;428-If-MatchorIdempotency-Keyis missing;429- request limit exceeded;503- service temporarily unavailable.
Read X-RateLimit-Limit and X-RateLimit-Remaining. For 429, use Retry-After when it is returned and an increasing delay for subsequent attempts. Mask the Client Secret, document secrets and file content in logs.
Documents - complete integration workflow
- Create a key in Settings - API - API Keys and grant only the scopes required by the integration.
- Set
BASE_URLto the public Codenica Cloud address or the On-Premise address provided by the administrator. - Send
GET /api/v1/contextand confirm the correct database, caller, scopes and limits. - Read
GET /api/v1/documents/schemaand select the document type, required fields and accepted values. - Read the paginated and filtered list or fetch a document by UUID.
- Create an invoice with a unique
Idempotency-Key, save its UUID and ETag, and repeat the identical request after a timeout. - Update the record only with the current
If-Matchand save the new ETag after every change. - Add, read and remove relationships allowed by the schema.
- Use the dedicated file endpoints, keeping the current ETag and distinguishing an attachment from file deletion.
- For larger sets, use
stats,valuesanddocuments:batch, then inspect every operation result. - Before deletion, read the document again, confirm the correct ETag and use a new idempotency key.
This workflow lets you move invoice and other document handling into an accounting system, document workflow, ERP or custom integration application.
