Solutions in Codenica API

Start working with solutions through Codenica API by creating a key in Codenica settings. If you have not created a key yet, open Codenica API - introduction in a new tab. It explains the shared rules for issuing keys, storing the secret and authenticating requests.

The technical module name is solutions, and the type of an individual object is solution. A solution is a knowledge-base entry. It can contain an instruction, a description of a procedure, references and supporting files. It is not a workflow object such as a Ticket, Change, Problem or Release, so do not copy their status, priority or escalation fields into a solution.

The following sections cover the API address, scopes, context, schema, fields, lists, filtering, creation, idempotency, ETag, editing, batch operations, relationships with Problems, author and editor information, files, ratings and deletion.

The examples use the identifier PUBLIC-API-SOLUTION-20260905134845. Replace it with your own identifier and adapt email addresses, IDs and field values to the data in your database.


Solutions - API address and installation choice

All routes for solutions start with:

{BASE_URL}/api/v1/solutions

BASE_URL is the Codenica server address without the final /api/v1. For Codenica Cloud, use the public domain assigned to the relevant company:

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 installation under a company domain, through a reverse proxy, with HTTPS or on another port, use the exact address provided for that installation:

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

Do not use localhost when the integrating program runs on a different computer than the API. The correct database is selected from the address used by the integration. Do not send tenantId in the body or query string.


Solutions - API key and licence limits

Create an API key in Codenica under Settings - API - API Keys. The secret is shown only once, immediately after the key is created or rotated. At that moment, save the Client ID and Client Secret in the secure storage used by the integration.

Codenica API is available with the Plus and Enterprise licences. Plus allows up to 50 active keys, while Enterprise allows up to 100. Starter does not include Codenica API. Create a separate key for each application and environment so that you can manage its scopes, rotate its secret or remove its access independently.

Licence
API access
Maximum active keys
Starter
Not available
0
Plus
Available
50
Enterprise
Available
100

Deleting a key removes its record and frees a slot within the limit. When its expiry date passes, the key stops authenticating, but it remains on the list until it is deleted. If you do not set an end date when creating a key, the default validity period is 90 days. The maximum validity period for one key is 5 years.


Solutions - authentication and secure requests

Authenticate every Codenica API request with the two key headers:

export CLIENT_ID="cna_your_client_id"
export CLIENT_SECRET="cns_your_client_secret"

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

An external integration does not need the administrator's JWT or cookies from the Codenica panel. Do not put the key in a repository, code delivered to a browser, a URL, shell history or logs. Use HTTPS outside local tests.

Keep meta.requestId from the response. It identifies a particular request for troubleshooting, but it is not the solution ID and must not be treated as a secret.


Solutions - checking the connection context

Before the first write, read the context. It confirms that the address points to the intended database and that the selected key has the required scopes:

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"

Verify the following values in the response:

  • data.apiVersion and data.contractVersion;
  • data.tenant.id, data.tenant.name and data.tenant.resolvedDomain;
  • data.caller.authentication equal to api_key;
  • the presence of solutions in data.capabilities.resources;
  • the scopes assigned to the key;
  • the page, batch, file and request limits.

If the context points to a different database or does not contain a required scope, stop the integration and correct the address or key. Scopes cannot be added to an individual request.


Solutions - scopes and permissions

Full solutions support requires scopes matching the operations your integration will use:

solutions:read
solutions:write
solutions:delete
solutions:schema
solutions:stats
solutions:relationships:read
solutions:relationships:write
solutions:users:read
solutions:files:read
solutions:files:write
solutions:technical:read
solutions:technical:write
solutions:rating:write
problems:read
users:read

For ordinary reads, solutions:read is enough. Schema and statistics use the separate solutions:schema and solutions:stats scopes. Reading relationships, authors, editors and files requires the corresponding read scopes. Write operations use the matching :write scopes.

  • solutions:relationships:read and solutions:relationships:write cover object relationships with Problems;
  • solutions:users:read and users:read cover author and editor information, depending on the configuration;
  • solutions:files:read and solutions:files:write cover listing, uploading, attaching and deleting files;
  • solutions:rating:write is required to submit or withdraw your own rating;
  • use technical scopes only when the integration needs fields marked as technical in the schema.

The problems:read scope is needed when the integration searches for a Problem to use as a relationship target. The Solutions module does not create or delete that Problem. Grant only the scopes the integration actually needs.


Solutions - schema and fields

The schema shows which fields can be read and written in the selected database. Retrieve it before building a form or field mapping:

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

The response includes, among other things, data.itemType, data.fields and data.relationshipTargets. The fixed itemType for this module is solution. For every field, check readable, writable, required, technical, unique and maxLength. Do not build a mapping only from the examples in this article.


Solutions - writable and system fields

When creating a record, you must provide title and category. The public business-field catalog includes:

customId
title
description
location
department
type
tags
section
category
visibility

Maximum lengths defined by the public schema:

Field
Maximum length
customId
500
title
1000
description
10000
location
300
department
300
type
300
tags
2000
section
300
category
300
visibility
100

The following values are read-only and do not belong in an ordinary PATCH payload:

helpful
notHelpful
totalFiles
creator
updater
importId
importSource
dateImported

id and itemType are part of the resource envelope. dateCreated and dateUpdated are system data. Do not try to change them through attributes.


Solutions - core endpoints

The main routes for the solutions module are:

GET    /api/v1/solutions
POST   /api/v1/solutions
GET    /api/v1/solutions/{SOLUTION_ID}
PATCH  /api/v1/solutions/{SOLUTION_ID}
DELETE /api/v1/solutions/{SOLUTION_ID}
GET    /api/v1/solutions/schema
GET    /api/v1/solutions/stats
GET    /api/v1/solutions/values
GET    /api/v1/solutions/{SOLUTION_ID}/relationships
POST   /api/v1/solutions/{SOLUTION_ID}/relationships
POST   /api/v1/solutions/{SOLUTION_ID}/relationships:batch
GET    /api/v1/solutions/{SOLUTION_ID}/user-relationships
GET    /api/v1/solutions/{SOLUTION_ID}/files
POST   /api/v1/solutions/{SOLUTION_ID}/files
GET    /api/v1/solutions/{SOLUTION_ID}/files/{FILE_ID}/content
POST   /api/v1/solutions/{SOLUTION_ID}/rating

Every route requires authentication. Mutating operations also require Idempotency-Key, while version-protected operations require the current If-Match. Check the schema and context response for the exact requirements of the operation you are about to call.


Solutions - listing and pagination

The list is paginated. This example retrieves the first page and sorts solutions by title:

curl --request GET --url "$BASE_URL/api/v1/solutions?itemType=solution&page=1&pageSize=25&sort=title&direction=asc" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

The response includes, among other values:

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

Retrieve subsequent pages while data.hasNextPage is true. Do not assume that the number of records on the first page is the complete list. Adjust pageSize to the limit returned by context instead of always requesting the maximum.


Solutions - search, filters and sorting

You can filter by, among other fields, ids, customId, title, description, location, department, type, tags, section, category, visibility, helpful, notHelpful, createdAfter, createdBefore, updatedAfter and updatedBefore. URL-encode values containing spaces, commas or special characters.

Example: list solutions in the Public API category:

curl --request GET --url "$BASE_URL/api/v1/solutions?category=Public%20API&page=1&pageSize=25&sort=title&direction=asc" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

The search parameter searches textual solution data, including the title, description, type, tags, section, category, visibility, location and department:

curl --request GET --url "$BASE_URL/api/v1/solutions?search=backup&page=1&pageSize=25" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Sort only by a field exposed by the schema. Do not assume that every field displayed in the form can be used as a sort parameter.


Solutions - field projection and included data

If the integration needs only part of the record, limit the response with fields:

curl --request GET --url "$BASE_URL/api/v1/solutions?customId=PUBLIC-API-SOLUTION-20260905134845-SOURCE&fields=title%2Ccategory%2Cvisibility&page=1&pageSize=10" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

You can retrieve one record together with its files, relationships and author or editor information:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}?fields=title%2Ccategory%2Cdescription%2Cvisibility&include=files%2Crelationships%2Cusers" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Allowed include values are files, relationships and users. Each one requires the corresponding scope. fields=* requests all available fields, but technical fields appear only when the relevant technical scope is granted.


Solutions - statistics and field values

Use statistics to count records and group them by a field:

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

The response includes, among other values, total, field and a values array. The value category=Public API can be a convenient filter for demonstration data.

To retrieve distinct values for a field, use the values route:

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

limit must be within the range supported by the API. The current range for these endpoints is 1 to 500. Statistics and field-value requests are read-only and do not change solutions.


Solutions - creating a record

When creating a record, put the technical type solution in the body and writable fields inside attributes. The minimum payload requires title and category:

{
  "itemType": "solution",
  "attributes": {
    "customId": "PUBLIC-API-SOLUTION-20260905134845-SOURCE",
    "title": "PUBLIC-API-SOLUTION-20260905134845 integration knowledge article",
    "description": "Created through the Codenica Public API Solutions flow.",
    "location": "Warsaw",
    "department": "IT",
    "type": "How-to",
    "tags": "public-api,solution,integration",
    "section": "Integrations",
    "category": "Public API",
    "visibility": "team"
  }
}

Save the content as solution-create.json and send it:

curl --request POST --url "$BASE_URL/api/v1/solutions" \
  --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: public-api-solution-create-20260905134845" \
  --data-binary @solution-create.json

A successful creation returns 201 Created. The response contains the solution UUID, data.itemType=solution, saved attributes, system dates and data.meta.etag. In most integrations, let the system assign the id.


Solutions - safely retrying creation

If the result of a request is uncertain, repeat exactly the same payload with the same Idempotency-Key. This prevents the integration from creating a second solution:

curl --request POST --url "$BASE_URL/api/v1/solutions" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: public-api-solution-create-20260905134845" \
  --data-binary @solution-create.json

Use the same key only for the same intention and the same body. Generate a new key for a new solution or a new payload. After a timeout, do not change the key before checking whether the first write completed on the server.


Solutions - reading and ETag

Before editing, changing a relationship, operating on a file or submitting a rating, retrieve the current record:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}?fields=title%2Ccategory%2Cdescription%2Cvisibility%2Ctags" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

The ETag is returned in the HTTP header and in the response envelope:

ETag: "..."
data.meta.etag: "..."
meta.etag: "..."

Save the value from the response as CURRENT_ETAG and use it in the next request that changes data. After a successful change, retrieve a new ETag. The old ETag is no longer current.


Solutions - partial update with If-Match

PATCH changes only the fields passed in attributes. This example updates the description, visibility, tags and section:

{
  "attributes": {
    "description": "Updated through the Solutions Public API flow.",
    "visibility": "internal",
    "tags": "public-api,solution,updated",
    "section": "Updated integrations"
  }
}
curl --request PATCH --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}" \
  --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-solution-update-20260905134845" \
  --data-binary @solution-update.json

A successful update returns 200 and a new ETag. Do not send read-only fields or technical data handled by separate endpoints in an ordinary PATCH.


Solutions - stale ETag and missing If-Match

The API protects a solution from overwriting a newer version. An old ETag is rejected:

If two processes read the same solution and one saves first, the second has a stale ETag. An attempt to write with this value is rejected:

HTTP 412 Precondition Failed
code: if_match_failed

After HTTP 412, read the record again, decide how to merge the changes, and only then send a new PATCH. A request rejected because of a stale ETag must not change the data.

A mutation without the required header returns:

HTTP 428 Precondition Required
code: if_match_required

Do not bypass this requirement by sending an empty value. Read the current record first and use its exact ETag.


Solutions - batch operations

A batch lets you perform several independent operations in one request. This example creates two solutions:

{
  "items": [
    {
      "operation": "create",
      "create": {
        "itemType": "solution",
        "attributes": {
          "customId": "PUBLIC-API-SOLUTION-BATCH-A",
          "title": "Batch Solution A",
          "category": "Public API",
          "description": "Batch-created Solution A",
          "type": "How-to",
          "visibility": "team"
        }
      }
    },
    {
      "operation": "create",
      "create": {
        "itemType": "solution",
        "attributes": {
          "customId": "PUBLIC-API-SOLUTION-BATCH-B",
          "title": "Batch Solution B",
          "category": "Public API",
          "description": "Batch-created Solution B",
          "type": "Reference",
          "visibility": "team"
        }
      }
    }
  ]
}
curl --request POST --url "$BASE_URL/api/v1/solutions: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-solution-batch-create-20260905134845" \
  --data-binary @solutions-batch.json

For an update, an item contains operation=update, id, ifMatch and update.attributes:

{
  "items": [
    {
      "operation": "update",
      "id": "{SOLUTION_A_ID}",
      "ifMatch": "{SOLUTION_A_ETAG}",
      "update": {
        "attributes": {
          "description": "Batch update A"
        }
      }
    }
  ]
}

For a deletion, an item contains operation=delete, id and the current ifMatch. The response can contain succeeded and failed, and a partial result can use HTTP 207 Multi-Status. Check every item separately. A batch is not a transaction.


Solutions - relationships with Problems only

Solutions support object relationships only with the Problems module. A typical target returned by the schema is:

targetDataSet: problems
targetItemType: problem

Do not assume that a solution can be linked through these endpoints to an Asset, Document, Client, Vendor, Ticket, Change or Release. If the schema for the selected database does not return a target, the integration must not use it.

First find a readable Problem, using a public sort field:

curl --request GET --url "$BASE_URL/api/v1/problems?itemType=problem&page=1&pageSize=10&sort=subject&direction=asc" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

The demonstration flow used the Problem ID 9793181f-a225-4928-8062-80d6e69cb792. Your integration should find a current target instead of treating this UUID as a permanent value.


Solutions - adding, reading and removing relationships

The body for adding a relationship directly can look like this:

{
  "targetId": "9793181f-a225-4928-8062-80d6e69cb792",
  "targetDataSet": "problems",
  "targetItemType": "problem",
  "relationshipType": "related"
}

Read the current solution ETag before each mutation:

curl --request POST --url "$BASE_URL/api/v1/solutions/{SOLUTION_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: public-api-solution-problem-relation-add-20260905135119" \
  --data-binary @solution-problem-relation.json

A successful add returns 201 Created. Read the relationship with a separate route:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/relationships?targetDataSet=problems&targetItemType=problem&page=1&pageSize=50" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Direct removal requires the current ETag and the target identifier:

curl --request DELETE --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/relationships/problems/9793181f-a225-4928-8062-80d6e69cb792?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-solution-problem-relation-delete-20260905135119"

A successful removal returns 200 with data=true. Read the collection again after the operation.


Solutions - batch relationships with Problems

Use the relationships:batch route to add and remove relationships in one request:

{
  "add": [
    {
      "targetId": "9793181f-a225-4928-8062-80d6e69cb792",
      "targetDataSet": "problems",
      "targetItemType": "problem",
      "relationshipType": "related"
    }
  ],
  "remove": []
}
curl --request POST --url "$BASE_URL/api/v1/solutions/{SOLUTION_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: public-api-solution-problem-relation-batch-add-20260905135119" \
  --data-binary @solution-problem-relation-batch.json

To remove a relationship through batch, leave add empty and move the item to remove:

{
  "add": [],
  "remove": [
    {
      "targetId": "9793181f-a225-4928-8062-80d6e69cb792",
      "targetDataSet": "problems",
      "targetItemType": "problem",
      "relationshipType": "related"
    }
  ]
}

The response contains added, removed and skipped counters. After adding, check added=1; after removing, check removed=1. Use a fresh ETag before every subsequent write.


Solutions - author and editor relationships

User relationships in this module are authorship and last-editor metadata. The supported types are author and editor:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/user-relationships?page=1&pageSize=50" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

A list item can contain:

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

The author and editor relationships are read from the solution's creator and updater fields. The public Solutions module does not provide add, update or delete endpoints for them. Do not try to create agent, watcher, appUserRequester or clientRequester relationships, because those types belong to other objects.


Solutions - files

Before every file operation, read the current solution and its ETag. List the files with:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/files?page=1&pageSize=100" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

A file list item can contain id, name, fileName, contentType, size, width, height, relationshipType, isMain and downloadUrl. For Solutions, isMain is always false. The module does not provide a set-main endpoint, so do not try to designate a main file.

Upload a file with multipart/form-data:

curl --request POST --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/files?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-solution-file-20260905134845" \
  --form "[email protected];type=text/plain"

A successful upload returns 201 Created and a file resource. The example uses solution-one.txt with type text/plain. Uploading changes the solution version, so read the new ETag afterwards.

Download the content through the authenticated path:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/files/{FILE_ID}/content" \
  --header "Accept: application/octet-stream" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --output solution-one-downloaded.txt

Treat downloadUrl as an API path, not as a public anonymous link. If a file already exists in the same file space, you can attach it to another solution:

curl --request POST --url "$BASE_URL/api/v1/solutions/{TARGET_SOLUTION_ID}/files/{FILE_ID}?relationshipType=manual" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $TARGET_CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-solution-file-attach-20260905134845"

For an attachment, use the ETag of the target solution, not the record from which the file originated. Deleting a file requires the current ETag:

curl --request DELETE --url "$BASE_URL/api/v1/solutions/{SOLUTION_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: public-api-solution-file-delete-20260905134845"

After receiving 200, read the file list to confirm that the file is no longer returned.


Solutions - usefulness rating

A rating is a separate mutation. You can mark a solution as helpful, as not helpful or withdraw your own rating:

{
  "rating": 1
}
  • 1 - helpful;
  • 0 - not helpful;
  • -1 - withdraw your own rating.

Mark a solution as helpful:

curl --request POST --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/rating" \
  --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-solution-rating-helpful-20260905134845" \
  --data '{"rating":1}'

Withdraw your own rating:

curl --request POST --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}/rating" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $RATING_CURRENT_ETAG" \
  --header "Idempotency-Key: public-api-solution-rating-reset-20260905134845" \
  --data '{"rating":-1}'

Both operations require an ETag. The response contains the current helpful and notHelpful counters and a new ETag. Do not update helpful or notHelpful through an ordinary PATCH.


Solutions - deleting a record

Deletion is irreversible, so first read the record and obtain its current ETag:

curl --request DELETE --url "$BASE_URL/api/v1/solutions/{SOLUTION_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-solution-delete-20260905134845"

A successful response is 200 with data=true. Then perform a verification GET and check a filtered list:

curl --request GET --url "$BASE_URL/api/v1/solutions/{SOLUTION_ID}" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

curl --request GET --url "$BASE_URL/api/v1/solutions?customId=PUBLIC-API-SOLUTION-20260905134845-SOURCE&page=1&pageSize=10" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

After deletion, the individual GET returns 404 Not Found with the solution_not_found code, and the filtered list should contain totalItems=0. Verify or clean up relationships and files before removing a record when the integration needs to preserve an audit trail.


Solutions - errors, limits and security

API errors use the Problem Details format. The main fields are status, code, detail and requestId:

{
  "type": "https://docs.codenica.com/errors/if_match_failed",
  "title": "Precondition failed.",
  "status": 412,
  "detail": "The supplied ETag is not the current solution version.",
  "instance": "/api/v1/solutions/{SOLUTION_ID}",
  "code": "if_match_failed",
  "requestId": "..."
}
HTTP
Meaning
Response
400
invalid field, filter, body or relationship
correct the request according to the schema
401
missing or invalid authentication
check the address and key
403
missing scope or database access
change the scope or user's permissions
404
solution, Problem, file or relationship target does not exist or is not visible
verify the UUID and installation address
409
identifier or existing relationship conflict
read the state and decide whether the conflict is expected
412
stale ETag
read the record and obtain a new ETag
413
file or body is too large
check the limit in context
422
a field value or existing flow rejected the operation
inspect code and detail
428
missing If-Match or Idempotency-Key
add the correct header
429
request limit exceeded
use backoff and Retry-After
500
server error
keep requestId and do not repeat a mutation without idempotency

Read the X-RateLimit-Limit and X-RateLimit-Remaining headers. After 429, respect Retry-After when it is returned and use controlled retries with increasing delays.

Solutions can contain operational data and internal instructions. Minimize the selected fields, use HTTPS and limit the key to the intended database. Store Client ID and Client Secret outside source code, do not write them to logs and do not send them in support requests.


Solutions - integration sequence

  1. Determine the actual Cloud or On-Premise address and set BASE_URL.
  2. Create a separate key for the application and environment under Settings - API - API Keys.
  3. Grant only the scopes needed for reading, writing, relationships, files or ratings.
  4. Send GET /api/v1/context and check the database, caller, scopes and limits.
  5. Retrieve GET /api/v1/solutions/schema and build the field mapping.
  6. Retrieve a list or search for an existing solution.
  7. Create a record with POST and a unique Idempotency-Key.
  8. Save the UUID and ETag from the response.
  9. Refresh the ETag before every update, relationship change, file operation, rating or deletion.
  10. Create object relationships only with a Problem returned by the schema.
  11. Read author and editor relationships only, because the public module does not provide write endpoints for them.
  12. After each mutation, read the result and save the new ETag.
  13. For 412, retrieve the record, resolve the conflict and only then retry the operation.
  14. For a batch, inspect every item because a partial error does not roll back successful items.
  15. Before deletion, confirm the current ETag and afterwards verify 404 and an empty customId list.

This sequence lets you synchronize knowledge-base solutions with another system without relying on assumptions about fields, relationships or system data.