Work Tasks in Codenica API

Before sending the first Work Task request, create an API key in your Codenica settings. If you do not have one yet, open Codenica API - introduction in a new tab. That article covers the shared rules for key creation, authentication, API addresses and secure secret storage.

A Work Task records a specific activity, responsibility or piece of work to be completed. One record can contain a due date, status, priority, category, description, location, department, link and tags. A Work Task can also be linked to other objects used in Service Desk work and asset management.

In the API contract, one record has itemType equal to worktask, while the endpoint collection is called worktasks. The examples contain safe demonstration values. Replace identifiers, addresses and dates with values from your integration.


Work Tasks - API address and installation choice

All Work Task routes start with:

{BASE_URL}/api/v1/worktasks

BASE_URL is the Codenica application address without the /api/v1 suffix. Do not append a database name or company identifier to the address.

Codenica Cloud: use the domain or subdomain assigned to the company:

export BASE_URL="https://{your-company-domain}"

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

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

If the administrator exposed the installation through a company domain, HTTPS, a reverse proxy or another port, use the exact address provided for that installation. Details are available in the Codenica On-Premise installation guide. Use localhost only in a deliberate local test environment where the HTTP client and API run on the same computer.

Do not send tenantId in the body or query parameters. The correct database is selected from the request address and host.


Work Tasks - API key and licence limits

Create the key in Settings -> API -> API Keys. A separate key for each application and environment makes access easier to control. Give the key a clear name and select only the scopes needed for Work Tasks.

Licence
Codenica API access
Maximum number of keys
Starter
No
0
Plus
Yes
50
Enterprise
Yes
100

Deleting a key removes its record and frees a place within the limit. Expiration stops authentication, but it does not replace keeping the key list tidy. If no end date is selected, the default active period is 90 days; the maximum period for one key is 5 years.


Work Tasks - request authentication

Authenticate every Codenica API request with two headers:

X-Codenica-Client-Id: {CLIENT_ID}
X-Codenica-Client-Secret: {CLIENT_SECRET}
Accept: application/json

Example of the first read:

export PUBLIC_API_CLIENT_ID="cna_your_client_id"
export PUBLIC_API_CLIENT_SECRET="cns_your_client_secret"

curl --fail-with-body --silent --show-error \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/context"

An external integration does not need a panel session or the user's Bearer JWT. Store the secret on the server side or in a secrets manager. Do not place it in browser code, a repository, a URL, shell history or logs.


Work Tasks - check the connection context

Read the context before downloading a list or creating the first Work Task. This confirms that the address leads to the correct database and that the key has the required scopes and limits.

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/context" | jq

Verify data.tenant.id, data.tenant.name, data.tenant.subdomain, data.tenant.resolvedDomain, data.caller.clientId and data.caller.scopes in the response. Also check that the capabilities include supportsRelationships, supportsFiles, supportsETag and supportsIdempotency.

{
  "data": {
    "apiVersion": "v1",
    "caller": {
      "authentication": "api_key",
      "clientId": "{CLIENT_ID}",
      "scopes": [
        "worktasks:read",
        "worktasks:write"
      ]
    },
    "capabilities": {
      "supportsETag": true,
      "supportsIdempotency": true,
      "supportsRelationships": true,
      "supportsFiles": true
    }
  },
  "meta": {
    "requestId": "{REQUEST_ID}"
  }
}

If the context points to another company or does not include the required scope, stop the integration and correct the address or key. Do not try to change the database by adding a foreign identifier to the body.


Work Tasks - schema and supported fields

The schema is the source of truth for the current Work Task configuration. It returns field types, required values, writability, technical fields and relationship targets available in the installation.

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/schema" | jq
{
  "data": {
    "itemType": "worktask",
    "fields": [
      {
        "name": "customId",
        "type": "string",
        "readable": true,
        "writable": true,
        "required": false
      },
      {
        "name": "title",
        "type": "string",
        "readable": true,
        "writable": true,
        "required": false
      }
    ],
    "relationshipTargets": [
      {
        "targetDataSet": "assets",
        "targetItemType": "asset"
      },
      {
        "targetDataSet": "tickets",
        "targetItemType": "ticket"
      }
    ]
  }
}

Do not assume that every database has the same configuration. Before mapping fields, read the current schema and respect readable, writable, required, technical and maxLength.


Work Tasks - writable and system fields

The following fields are intended for attributes. If the schema for the current installation gives different limits, it takes priority.

Field
Type
Use
customId
string
Identifier assigned by the integrating system.
dateDue
date-time
Work Task due date.
dateEnd
date-time
Date when the work was completed.
location
string
Place where the work is carried out.
department
string
Responsible department or unit.
tag
string
Tags; maximum 2000 characters.
link
string
Link to the source or details in another application.
title
string
Short Work Task title.
status
string
Process status.
priority
string
Priority.
category
string
Work Task category.
description
string
Description; maximum 10000 characters.

The pin field is read-only and is changed through the dedicated /pin route. Technical fields such as authorId, agentId, workTimeId, creator, updater, dateCreated, dateUpdated, importId, importSource and dateImported are filled by the system. Do not send them in a normal create request or PATCH. The itemType value must always be worktask.


Work Tasks - main endpoints

The list below shows the main operations available for the worktask object. Add only the scope required for the operation you want to perform.

Method
Path
Use
GET
/api/v1/worktasks
List, pagination and filters.
POST
/api/v1/worktasks
Create a Work Task.
GET
/api/v1/worktasks/schema
Field and relationship schema.
GET
/api/v1/worktasks/stats
Field statistics.
GET
/api/v1/worktasks/values
Field values with search.
GET
/api/v1/worktasks/{id}
Read one Work Task.
PATCH
/api/v1/worktasks/{id}
Partial update.
DELETE
/api/v1/worktasks/{id}
Delete a Work Task.
POST
/api/v1/worktasks:batch
Create, update and delete in one request.
GET/POST
/api/v1/worktasks/{id}/relationships
Read or add object relationships.
POST
/api/v1/worktasks/{id}/relationships:batch
Add and remove multiple relationships.
GET
/api/v1/worktasks/{id}/user-relationships
Read the author and agent.
GET/POST/DELETE
/api/v1/worktasks/{id}/files...
List, upload, attach, detach and download file content.
POST
/api/v1/worktasks/{id}/pin
Pin or unpin a Work Task.

Work Tasks - listing and pagination

Read the Work Task list page by page. Set sorting explicitly so that subsequent reads use a predictable order:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks?page=1&pageSize=25&sort=dateCreated&direction=desc" | jq

Read data.items, page, pageSize, totalItems, totalPages and hasNextPage in the response. When hasNextPage is true, request the next page. Check the maximum page size in data.capabilities.limits.maxPageSize from the context.

Use ids to request selected UUIDs. For synchronization, it is better to keep a stable customId in the integrating application and then store the UUID returned by Codenica API.


Work Tasks - search and filters

The list supports text search, matching selected fields and a structural filter. Common parameters include customId, search, title, status, priority, category, location, department, tag, createdAfter, createdBefore, updatedAfter and updatedBefore.

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks?search=workstation&filter=status%3Aeq%3AOpen&sort=dateDue&direction=asc&page=1&pageSize=25" | jq

A filter has the form field:operator:value. Examples of operators:

status:eq:Open
priority:ne:Low
title:startswith:Prepare
description:contains:workstation
dateDue:gte:2026-09-01T00:00:00Z

The shortcuts =, !=, ge, le, sw and ew correspond to equality, inequality, greater than or equal, less than or equal, startswith and endswith. URL-encode values containing special characters.


Work Tasks - field selection and included data

Use fields to limit the fields returned in a record. This keeps the response smaller and easier to process:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks?fields=customId,title,status,priority,dateDue&page=1&pageSize=25" | jq

Use include when related data is needed:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/{WORKTASK_ID}?fields=%2A&include=files%2Crelationships%2Cusers" | jq

Included data does not increase the key's permissions. To see files, relationships or users, the key needs worktasks:files:read, worktasks:relationships:read and worktasks:users:read. Use full fields=* only when technical fields are actually needed.


Work Tasks - statistics and field values

Statistics help build summaries without downloading the entire collection. Example: count Work Tasks by status:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/stats?field=status&limit=20" | jq
{
  "data": {
    "total": 42,
    "field": "status",
    "values": [
      { "value": "Open", "count": 12 },
      { "value": "In progress", "count": 18 },
      { "value": "Closed", "count": 12 }
    ]
  },
  "meta": {
    "requestId": "{REQUEST_ID}"
  }
}

The values endpoint returns field values matching a search. This is useful, for example, for suggestions in a form:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/values?field=category&search=oper&limit=20" | jq

Both routes are read-only and require the worktasks:stats scope.


Work Tasks - minimal creation

A minimal write should contain itemType and an attributes object. In practice, set your own customId and title immediately:

{
  "itemType": "worktask",
  "attributes": {
    "customId": "ERP-WORKTASK-2026-0042",
    "title": "Prepare workstation",
    "status": "Open",
    "priority": "High",
    "category": "IT"
  }
}

Request that creates the record:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Accept: application/json, application/problem+json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "Idempotency-Key: erp-worktask-create-2026-0042" \
  --data-raw '{
    "itemType": "worktask",
    "attributes": {
      "customId": "ERP-WORKTASK-2026-0042",
      "title": "Prepare workstation",
      "status": "Open",
      "priority": "High",
      "category": "IT"
    }
  }' \
  "$BASE_URL/api/v1/worktasks" | jq

A successful response has status 201 Created. Save data.id and the record ETag for further work.


Work Tasks - complete creation

The following example records the data normally needed to pass a Work Task from a work planning system:

{
  "itemType": "worktask",
  "attributes": {
    "customId": "ERP-WORKTASK-2026-0042",
    "title": "Prepare a workstation for a new employee",
    "description": "Install the computer, configure network access and confirm that the workstation is ready.",
    "status": "Open",
    "priority": "High",
    "category": "Onboarding",
    "dateDue": "2026-09-30T12:00:00Z",
    "location": "Krakow",
    "department": "IT",
    "tag": "onboarding,workstation",
    "link": "https://portal.example.com/tasks/ERP-WORKTASK-2026-0042"
  }
}

Status, priority and category values should match the configuration used in your database. The API does not create a new dictionary automatically just because an integration sends a new name.

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Accept: application/json, application/problem+json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "Idempotency-Key: erp-worktask-create-2026-0042" \
  --data-binary @worktask.json \
  "$BASE_URL/api/v1/worktasks" | jq

Work Tasks - Idempotency-Key and safe retries

Every mutation made with an API key requires an Idempotency-Key header. The value identifies one business intent. For a retry of the same request, keep the same key and do not change the body. Generate a different value for a new Work Task or another operation.

--header "Idempotency-Key: erp-worktask-create-2026-0042"

If the connection breaks after the request is sent, first repeat the identical request with the same key. Do not immediately create a new key, because that may create a duplicate. Without the header, the mutation ends with 428 and code idempotency_key_required.


Work Tasks - read one record

After creation, read the record using the UUID returned in data.id:

export WORKTASK_ID="{UUID_FROM_CREATE_RESPONSE}"

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID?fields=customId,title,status,priority,category,dateDue,description" | jq

A single record contains id, itemType, attributes and meta. Read the ETag from meta; it is needed for the next mutation.

{
  "data": {
    "id": "{WORKTASK_ID}",
    "itemType": "worktask",
    "attributes": {
      "customId": "ERP-WORKTASK-2026-0042",
      "title": "Prepare a workstation for a new employee",
      "status": "Open"
    },
    "meta": {
      "customId": "ERP-WORKTASK-2026-0042",
      "etag": "{CURRENT_ETAG}"
    }
  },
  "meta": {
    "requestId": "{REQUEST_ID}",
    "etag": "{CURRENT_ETAG}"
  }
}

Work Tasks - update with ETag and If-Match

Before changing a record, read its current version and keep the exact ETag value, including quotation marks when they are part of it:

ETAG=$(curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID?fields=customId,title,status,priority,description" \
  | jq -r '.data.meta.etag // .meta.etag')

PATCH changes only the selected attributes. Save the new ETag after success:

curl --fail-with-body --silent --show-error \
  --request PATCH \
  --header "Accept: application/json, application/problem+json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $ETAG" \
  --header "Idempotency-Key: erp-worktask-update-2026-0042" \
  --data-raw '{
    "attributes": {
      "title": "Configure workstation for a new employee",
      "status": "In progress",
      "priority": "Normal",
      "description": "The computer and network access are being configured."
    }
  }' \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID" | jq

Do not use an ETag saved before another change. Every successful mutation can change the record version.


Work Tasks - stale or missing ETag

If another person or integration changed the Work Task, an old ETag ends with 412 Precondition Failed and code if_match_failed. The API should not apply the rejected change.

{
  "type": "https://docs.codenica.com/errors/if_match_failed",
  "title": "Precondition failed.",
  "status": 412,
  "detail": "The supplied ETag is not the current workTask version.",
  "code": "if_match_failed",
  "requestId": "{REQUEST_ID}"
}

PATCH, DELETE, relationships, files and pinning without the required If-Match return 428 Precondition Required with code if_match_required. After 412, read the record again, decide whether to keep the local change, and only then send a new request.


Work Tasks - pin and unpin

The pin field is read-only inside attributes. Change it through the dedicated endpoint:

POST /api/v1/worktasks/{WORKTASK_ID}/pin

Pin at level 3:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $ETAG" \
  --header "Idempotency-Key: erp-worktask-pin-2026-0042" \
  --data '{"pin":3}' \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/pin" | jq

The value null unpins the Work Task:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $NEW_ETAG" \
  --header "Idempotency-Key: erp-worktask-unpin-2026-0042" \
  --data '{"pin":null}' \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/pin" | jq

Read the Work Task again after either operation because its ETag may change.


Work Tasks - author and agent user relationships

The user relationship has its own route and is used to read the Work Task author and assigned agent:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/user-relationships?page=1&pageSize=20" | jq

The collection contains only relationships with type author or agent. An example item:

{
  "targetId": "{USER_ID}",
  "targetDataSet": "users",
  "relationshipType": "agent",
  "displayName": "Anna Kowalska",
  "email": "[email protected]",
  "role": "Agent"
}

authorId and agentId are technical fields. Do not try to change them through a normal PATCH attributes. If the API version provides a separate assignment action, follow its schema and required scope.


Work Tasks - permitted object relationships

The Work Task schema exposes eleven object groups that can be relationship targets:

Target dataset
Example itemType
Purpose
assets
computer
Asset, such as a computer or device.
clients
client
Client.
vendors
vendor
Vendor.
documents
document
Document.
tickets
ticket
Ticket.
changes
change
Change.
problems
problem
Problem.
releases
release
Release.
notes
note
Note.
approvals
approval
Approval.
requesteditems
requesteditem
Requested item.

For assets, the type depends on the specific asset. The table uses computer as an example; read the actual itemType of the selected object before saving the relationship.


Work Tasks - choosing targetItemType and relationship format

targetItemType must match the actual type of the target object. The safest order is: read the target list or schema, read its itemType, and only then build the relationship body.

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/assets?page=1&pageSize=10&sort=dateCreated&direction=desc" | jq '.data.items[0] | {id, itemType}'

Work Task object relationships do not accept relationshipType. Send only the identifier, collection name and object type:

{
  "targetId": "{ASSET_ID}",
  "targetDataSet": "assets",
  "targetItemType": "computer"
}

Do not copy asset, document or task without checking the specific target. An incorrect type ends with a validation error.


Work Tasks - add, read and remove a relationship

Adding one asset relationship requires the current Work Task ETag and a separate idempotency key:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $ETAG" \
  --header "Idempotency-Key: erp-worktask-relation-assets-2026-0042" \
  --data '{
    "targetId": "{ASSET_ID}",
    "targetDataSet": "assets",
    "targetItemType": "computer"
  }' \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/relationships" | jq

Read the relationship with a dataset filter:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/relationships?targetDataSet=assets&page=1&pageSize=100" | jq

Remove one relationship:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $ETAG" \
  --header "Idempotency-Key: erp-worktask-relation-delete-assets-2026-0042" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/relationships/assets/{ASSET_ID}" | jq

After removal, the response should contain data=true. Adding a new relationship returns 201 Created; in some situations, pointing to an existing relationship again may return 200 OK.


Work Tasks - relationship batch

Add or remove multiple relationships in one request:

{
  "add": [
    {
      "targetId": "{DOCUMENT_ID}",
      "targetDataSet": "documents",
      "targetItemType": "document"
    }
  ],
  "remove": [
    {
      "targetId": "{ASSET_ID}",
      "targetDataSet": "assets",
      "targetItemType": "computer"
    }
  ]
}
curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $ETAG" \
  --header "Idempotency-Key: erp-worktask-relationships-batch-2026-0042" \
  --data-binary @worktask-relationships.json \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/relationships:batch" | jq
{
  "data": {
    "added": 1,
    "removed": 1,
    "skipped": 0
  },
  "meta": {
    "requestId": "{REQUEST_ID}"
  }
}

Relationship batches also do not accept relationshipType. Use the current Work Task ETag and read the item limit from the context. Save the new ETag if one is returned, then read the collection to verify the result.


Work Tasks - file list and upload

Files assigned to a Work Task use a separate endpoint group. Start by reading the list:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/files?page=1&pageSize=100" | jq

A list item includes id, fileName, contentType, size, relationshipType, isMain and downloadUrl. Upload a new file as multipart/form-data:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Accept: application/json, application/problem+json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $ETAG" \
  --header "Idempotency-Key: erp-worktask-file-upload-2026-0042" \
  --form "file=@./workstation-instructions.pdf;type=application/pdf" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/files?relationshipType=instruction" | jq

Upload requires worktasks:files:write, the current ETag and the file limit read from the context. For Work Tasks, the API sets isMain=false; do not assume a separate main-file operation.


Work Tasks - download and attach a file

Download file content through the content route and save it in binary mode:

curl --fail-with-body --silent --show-error \
  --output ./workstation-instructions-downloaded.pdf \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/files/{FILE_ID}/content"

If a file is already stored in the system, attach it to a second Work Task without uploading the content again. Read the second Work Task ETag first:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Accept: application/json, application/problem+json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $SECOND_WORKTASK_ETAG" \
  --header "Idempotency-Key: erp-worktask-file-attach-2026-0042" \
  "$BASE_URL/api/v1/worktasks/$SECOND_WORKTASK_ID/files/{FILE_ID}?relationshipType=reference" | jq

Attach creates a file relationship with the second Work Task. The same file can be visible in both records, and reference is the file relationship type, not an object relationship type.


Work Tasks - detach and delete a file

Detach the file from the second Work Task using its current ETag:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $SECOND_WORKTASK_ETAG" \
  --header "Idempotency-Key: erp-worktask-file-detach-2026-0042" \
  "$BASE_URL/api/v1/worktasks/$SECOND_WORKTASK_ID/files/{FILE_ID}" | jq

Detaching should return data=true and should not remove the file relationship from the source Work Task. To remove the file from the source, read its new ETag and run:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $SOURCE_WORKTASK_ETAG" \
  --header "Idempotency-Key: erp-worktask-file-delete-2026-0042" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID/files/{FILE_ID}" | jq

Check the file list after deletion. Work Tasks do not have a separate endpoint for setting a main file.


Work Tasks - batch operations for records

The /api/v1/worktasks:batch endpoint combines Work Task creation, updates and deletion. Each update or delete item has its own ETag:

{
  "items": [
    {
      "operation": "create",
      "create": {
        "itemType": "worktask",
        "attributes": {
          "customId": "ERP-WORKTASK-BATCH-A",
          "title": "Prepare access",
          "status": "Open",
          "priority": "Normal",
          "category": "IT"
        }
      }
    },
    {
      "operation": "update",
      "id": "{WORKTASK_ID}",
      "ifMatch": "{CURRENT_ETAG}",
      "update": {
        "attributes": {
          "title": "Prepare access - second stage",
          "status": "In progress"
        }
      }
    },
    {
      "operation": "delete",
      "id": "{OTHER_WORKTASK_ID}",
      "ifMatch": "{OTHER_CURRENT_ETAG}"
    }
  ]
}
curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "Idempotency-Key: erp-worktasks-batch-2026-0042" \
  --data-binary @worktasks-batch.json \
  "$BASE_URL/api/v1/worktasks:batch" | jq
{
  "data": {
    "items": [
      {
        "index": 0,
        "operation": "create",
        "status": 201,
        "id": "{CREATED_WORKTASK_ID}",
        "data": {
          "meta": {
            "etag": "{CREATED_ETAG}"
          }
        }
      }
    ],
    "succeeded": 1,
    "failed": 0
  },
  "meta": {
    "requestId": "{REQUEST_ID}"
  }
}

For a partial result, the API may return 207 Multi-Status. Go through data.items, inspect each item and retry only operations that actually need another attempt.


Work Tasks - delete a record

Before deletion, read the record again, check its UUID and current ETag, and use a new idempotency key:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "Accept: application/json, application/problem+json" \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  --header "If-Match: $CURRENT_ETAG" \
  --header "Idempotency-Key: erp-worktask-delete-2026-0042" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID" | jq

A successful response contains 200 OK and data=true. After deletion, verify that the record is no longer available:

curl --fail-with-body --silent --show-error \
  --header "X-Codenica-Client-Id: $PUBLIC_API_CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $PUBLIC_API_CLIENT_SECRET" \
  "$BASE_URL/api/v1/worktasks/$WORKTASK_ID"

The expected status is 404 Not Found. You can also filter the list with customId=ERP-WORKTASK-2026-0042 and confirm totalItems=0.


Work Tasks - errors, limits and safe order of operations

Problem responses use the Problem Details format. Use code in integration logic and keep requestId as well when reporting a problem. Do not put the secret or complete headers in logs.

HTTP
Code or situation
Response
400
validation_failed
Correct the body, field, filter or target. Do not retry without changing the data.
401
authentication_required
Check both headers, key activity and installation address.
403
Missing scope or access
Add the minimum missing scope or change the operation.
404
worktask_not_found
Check the UUID, database address and visibility scope.
404
file_not_found
Read the current file list.
409
Conflict
Read the current state and decide whether the operation can be repeated safely.
412
if_match_failed
Read the current ETag and do not overwrite changes automatically.
413
file_too_large
Check the limit in context and reduce the file.
428
if_match_required
Add the current If-Match to a mutation of an existing record.
428
idempotency_key_required
Add a unique Idempotency-Key to the mutation.
429
Rate limit exceeded
Read Retry-After and apply backoff.
500
internal_error
Keep requestId, limit retries and report the problem.
207
Partial batch
Check each item separately.

Read X-RateLimit-Limit and X-RateLimit-Remaining headers. At 429, use increasing delays with jitter, limit the number of attempts and never loop indefinitely for 400, 401, 403, 404 or 412.

Safe order of operations

  1. Set BASE_URL for the correct installation and read the context.
  2. Check scopes, limits, schema and the actual itemType of relationship targets.
  3. Create a Work Task with its own Idempotency-Key, then save the UUID and ETag.
  4. Read the current ETag before every change, relationship, file operation or pin action.
  5. Save the new ETag after a successful mutation and verify the result with a read.
  6. After synchronization, check the record by customId and delete demonstration data with a separate request.
  7. In n8n, use the HTTP Request node; keep the Client ID and Client Secret in credentials and pass the UUID, ETag and idempotency key between nodes.