Releases in Codenica API
Start working with releases 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 releases, and the type of an individual object is release. A release describes a planned publication or deployment of changes in an IT environment. In addition to descriptive data, it has its own group of fields for build, testing, results and implementation planning.
The following sections cover the address, scopes, schema, lists, filtering, creation, editing, ETags, batch operations, relationships, users, files, workflow actions, approvals and deletion of releases.
The examples use the identifier PUBLIC-API-RELEASE-20260905133117. Replace it with your own identifier and adjust email addresses, IDs and field values to the data in your database.
Releases - API address and installation choice
All release routes start with:
{BASE_URL}/api/v1/releasesFor Codenica Cloud, use the public address assigned to the relevant database:
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 exposed 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. Do not send tenantId in the body or query string. The correct database is selected from the address used by the integration.
Releases - 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 point, save the Client ID and Client Secret in the secure store 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 provide Codenica API. Create a separate key for each application and environment so that its scopes, secret rotation and access can be managed independently.
Expired or inactive keys remain visible until you use Delete, but they do not occupy an active place in the limit. Deleting a key is permanent. If you do not set an end date, the default active period is 90 days, and the maximum active period for one key is 5 years.
Releases - 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/releases?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 an administrator JWT or cookies from the Codenica panel. Do not put the key in a repository, code delivered to a browser, a URL, command history or logs. Use HTTPS outside local testing.
Keep meta.requestId from the response. It helps diagnose a specific request, but it does not replace the release identifier and must not be used as a secret.
Releases - checking the connection context
Read the context before the first write. This lets you verify that the address leads to the correct 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"Check the following in the response:
data.apiVersionanddata.contractVersion;data.tenant.id,data.tenant.nameanddata.tenant.resolvedDomain;data.caller.authenticationequal toapi_key;- the presence of
releasesindata.capabilities.resources; - the scopes assigned to the key;
- the page, batch, file and request limits.
If the context points to another database or does not contain a required scope, stop the integration and correct the address or key. Scopes cannot be granted by an individual request.
Releases - permission scopes
Select key scopes according to the operations the integration must perform. Full release support may use the following set:
releases:read
releases:write
releases:delete
releases:schema
releases:stats
releases:relationships:read
releases:relationships:write
releases:users:read
releases:users:write
releases:files:read
releases:files:write
releases:technical:read
releases:technical:write
releases:pin:write
releases:spam:write
releases:reopen:write
releases:rating:write
releases:escalation:write
releases:approval:writeFor ordinary reading, you need releases:read. Schema and statistics require releases:schema and releases:stats respectively. Relationships, users and files have separate read and write scopes. The pin, spam, reopen, rating, escalation and approval actions require their own operational scopes.
A relationship with another object also requires read access to the referenced module, for example assets:read, documents:read, tickets:read or notes:read. Grant scopes according to the principle of least privilege.
Releases - schema and planning fields
The schema shows which fields can be read and written in a given database. Retrieve it before preparing a form or field mapping:
curl --request GET --url "$BASE_URL/api/v1/releases/schema" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"For each field, check such properties as readable, writable, required, technical, unique and maxLength. The schema also returns dictionaries, available relationship targets and actions.
At present, the minimum release write requires subject and requesterEmail. Releases have an additional group of fields describing preparation and implementation planning:
datePlannedStartdatePlannedEndbuildPlantestPlantestResultsimplementationPlanSend date fields in ISO 8601 format. Do not copy diagnostic fields from problems or financial fields from other modules into a release request. Always compare the payload with the Releases schema.
Releases - essential endpoints
The most frequently used release routes are:
GET /api/v1/releases- release list;GET /api/v1/releases/{id}- one release;POST /api/v1/releases- create;PATCH /api/v1/releases/{id}- partial update;DELETE /api/v1/releases/{id}- delete;GET /api/v1/releases/schema- field and relationship schema;GET /api/v1/releases/stats- statistics;GET /api/v1/releases/values- values used by filters;POST /api/v1/releases:batch- create, update and delete operations.
Relationships, users, files, workflow actions and approvals have separate routes. This allows an integration to receive only the permissions it actually needs.
Releases - listing and pagination
Retrieve the list page by page. Even with a small number of records, provide the page number and size explicitly:
curl --request GET --url "$BASE_URL/api/v1/releases?page=1&pageSize=25" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"The response contains data.items and the page, pageSize, totalItems, totalPages and hasNextPage values. Retrieve the next pages while hasNextPage is true:
curl --request GET --url "$BASE_URL/api/v1/releases?page=2&pageSize=25&sort=dateUpdated&direction=desc" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"For synchronisation, sorting by dateUpdated and remembering the last processed records is usually the most convenient approach. Do not set pageSize above the limit returned in the context.
Releases - search, filters and sorting
You can combine list parameters. The following example searches for one release by its integration identifier, limits the result to the release type and a status, selects fields and then sorts by update date:
curl --get --url "$BASE_URL/api/v1/releases" \
--data-urlencode "itemType=release" \
--data-urlencode "customId=PUBLIC-API-RELEASE-20260905133117-SOURCE" \
--data-urlencode "status=Closed" \
--data-urlencode "sort=dateUpdated" \
--data-urlencode "direction=desc" \
--data-urlencode "page=1" \
--data-urlencode "pageSize=25" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"For day-to-day synchronisation, the search, status, priority, subject, requesterEmail, source, externalNumber, referenceNumber, createdAfter, createdBefore, updatedAfter and updatedBefore parameters are also useful when they are available in the current schema.
A structural filter has the format field:operator:value. Available operators are eq, ne, in, contains, startsWith, endsWith, empty, notEmpty, gt, gte, lt and lte:
curl --get --url "$BASE_URL/api/v1/releases" \
--data-urlencode "filter=status:eq:Closed" \
--data-urlencode "filter=buildPlan:contains:package" \
--data-urlencode "page=1" \
--data-urlencode "pageSize=25" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"URL-encode text values and dates according to URL rules. Do not assume that dictionaries are identical in two databases.
Releases - selecting fields and including data
The fields parameter limits the response to the fields needed by the integration. The include parameter adds related data:
curl --get --url "$BASE_URL/api/v1/releases/{RELEASE_ID}" \
--data-urlencode "fields=subject,requesterEmail,status,priority,datePlannedStart,datePlannedEnd,buildPlan,testPlan,testResults,implementationPlan" \
--data-urlencode "include=files,relationships,users" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"For the complete model, you can use fields=*. Including files, relationships and users requires the relevant read scopes. fields does not bypass access control or expose technical fields for which the key has no permission.
In the response, pay attention to data.id, data.itemType, data.attributes and data.meta. Read technical fields such as pin or isSpam, but change them through the dedicated actions described below.
Releases - statistics and dictionary values
Statistics can be used, for example, to check the distribution of releases by status. This is a read operation and does not modify records:
curl --get --url "$BASE_URL/api/v1/releases/stats" \
--data-urlencode "field=status" \
--data-urlencode "limit=20" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"Retrieve values of the buildPlan field separately when you need to build suggestions or filters:
curl --get --url "$BASE_URL/api/v1/releases/values" \
--data-urlencode "field=buildPlan" \
--data-urlencode "limit=20" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"Retrieve the dictionary first, then send the selected value in the body. This is especially important for status, priority, type and planning fields configured in the given database.
Releases - creating a record
Create a new release with POST /api/v1/releases. Put the technical type release in the body and writable fields in attributes. The example contains descriptive data, classification, integration identifiers and the complete planning group:
curl --request POST --url "$BASE_URL/api/v1/releases" \
--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-release-create-20260905133117" \
--data-raw '{
"itemType": "release",
"attributes": {
"customId": "PUBLIC-API-RELEASE-20260905133117-SOURCE",
"subject": "Public API integration release",
"requesterEmail": "[email protected]",
"description": "Release created through the Codenica Public API integration.",
"comments": "Example implementation plan for the Releases module.",
"source": "Public API",
"type": "Standard",
"status": "Closed",
"priority": "High",
"impact": "Medium",
"urgency": "High",
"severity": "High",
"services": "Codenica Public API",
"tags": "public-api,release",
"externalNumber": "EXT-PUBLIC-API-RELEASE-20260905133117",
"referenceNumber": "REF-PUBLIC-API-RELEASE-20260905133117",
"datePlannedStart": "2026-09-05T08:00:00Z",
"datePlannedEnd": "2026-09-05T10:00:00Z",
"buildPlan": "Release package preparation.",
"testPlan": "Functional tests before publication.",
"testResults": "Demonstration tests completed successfully.",
"implementationPlan": "Staged deployment with rollback available."
},
"customValues": [
{
"name": "description",
"valuePattern": "[release-integration] PUBLIC-API-RELEASE-20260905133117"
}
]
}'The minimum is subject and requesterEmail, unless the schema imposes additional requirements. After creation, save data.id and the ETag returned in the header and in data.meta.etag. The key secret is not part of the release response.
Releases - 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 release:
curl --request POST --url "$BASE_URL/api/v1/releases" \
--header "Content-Type: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "Idempotency-Key: public-api-release-create-20260905133117" \
--data-binary @release.jsonUse the same key only for the same intention and the same body. Generate a new key for a new release or a new payload. Do not change the key after a timeout before checking whether the first write completed on the server.
Releases - reading a record and its ETag
Read one release with all fields and included data:
curl --get --url "$BASE_URL/api/v1/releases/{RELEASE_ID}" \
--data-urlencode "fields=*" \
--data-urlencode "include=files,relationships,users" \
--header "Accept: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"Keep the ETag from the response header. It should match data.meta.etag and meta.etag in the response envelope. After every successful write, action, relationship change or file operation, retrieve or read the new ETag.
An ETag represents the version of one specific release. Do not use an ETag read for one release to modify another.
Releases - updating with If-Match
Updates are partial. Send only the fields that should change and put the current ETag in the If-Match header:
curl --request PATCH --url "$BASE_URL/api/v1/releases/{RELEASE_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: public-api-release-update-20260905133117" \
--data-raw '{
"attributes": {
"description": "Description updated by the integration.",
"status": "Closed",
"priority": "High",
"datePlannedStart": "2026-09-05T09:00:00Z",
"datePlannedEnd": "2026-09-05T11:00:00Z",
"buildPlan": "Updated package preparation plan.",
"testPlan": "Updated test scenario.",
"testResults": "Test results after the fix.",
"implementationPlan": "Updated implementation plan."
}
}'A valid If-Match returns HTTP 200 and a new ETag. Do not send read-only fields in an ordinary PATCH or technical fields handled by dedicated actions.
Releases - handling a stale If-Match
A release may be changed at the same time by the panel or another integration. The API protects it against accidental overwrites:
curl --request PATCH --url "$BASE_URL/api/v1/releases/{RELEASE_ID}" \
--header "Content-Type: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header 'If-Match: "old-etag"' \
--header "Idempotency-Key: public-api-release-stale-update-20260905133117" \
--data-raw '{"attributes":{"description":"This change requires a fresh read."}}'- 428 Precondition Required with code
if_match_requiredmeans that the requiredIf-Matchheader is missing. - 412 Precondition Failed with code
if_match_failedmeans that the supplied ETag is no longer current.
A rejected request should not change the release. After HTTP 412, read the record again, obtain the new ETag and decide whether to retry the change. Do not overwrite changes made by another person or process without checking them.
Releases - batch operations
The batch endpoint handles several independent items in one request. The following example creates two releases:
curl --request POST --url "$BASE_URL/api/v1/releases:batch" \
--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-release-batch-create-20260905133117" \
--data-raw '{
"items": [
{
"operation": "create",
"create": {
"itemType": "release",
"attributes": {
"customId": "PUBLIC-API-RELEASE-20260905133117-BATCH-A",
"subject": "Batch release A",
"requesterEmail": "[email protected]",
"source": "Public API",
"type": "Standard",
"status": "Open",
"priority": "Medium",
"datePlannedStart": "2026-09-05T11:00:00Z",
"datePlannedEnd": "2026-09-05T12:00:00Z",
"buildPlan": "Release A build plan",
"testPlan": "Release A test plan",
"testResults": "Release A test results",
"implementationPlan": "Release A implementation plan"
}
}
},
{
"operation": "create",
"create": {
"itemType": "release",
"attributes": {
"customId": "PUBLIC-API-RELEASE-20260905133117-BATCH-B",
"subject": "Batch release B",
"requesterEmail": "[email protected]",
"source": "Public API",
"type": "Standard",
"status": "Open",
"priority": "Low",
"datePlannedStart": "2026-09-05T13:00:00Z",
"datePlannedEnd": "2026-09-05T14:00:00Z",
"buildPlan": "Release B build plan",
"testPlan": "Release B test plan",
"testResults": "Release B test results",
"implementationPlan": "Release B implementation plan"
}
}
}
]
}'Check the batch response item by item. Do not treat HTTP 200 as proof that every item succeeded. Check succeeded, failed, IDs and errors for each item.
Updates and deletion use the same endpoint:
{
"items": [
{
"operation": "update",
"id": "{RELEASE_ID}",
"ifMatch": "\"{CURRENT_ETAG}\"",
"update": {
"attributes": {
"datePlannedStart": "2026-09-05T09:30:00Z",
"buildPlan": "Updated package preparation plan"
}
}
},
{
"operation": "delete",
"id": "{OTHER_RELEASE_ID}",
"ifMatch": "\"{OTHER_CURRENT_ETAG}\""
}
]
}For update and delete, use the ETag read for the specific record. The idempotency key identifies the whole batch request, not an individual item. A batch is not a transaction, so handle the result of each item separately.
Releases - relationships with objects
Available relationship targets are returned by /api/v1/releases/schema. The schema may include the assets, documents, changes, tickets, problems, releases, notes, approvals, worktasks and requesteditems collections.
The fact that a target appears in the schema does not mean that a usable record exists in the current database. Before adding a relationship, check permissions, the target ID and its itemType. For assets, documents, tickets, changes, problems and releases, use the relationship type returned by the schema, such as related. For notes, approvals, worktasks and requesteditems, relationshipType may be null. Do not force related when the schema does not specify it.
Adding several relationships through batch:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-relationship-batch-20260905133117" \
--data-raw '{
"add": [
{
"targetId": "{ASSET_ID}",
"targetDataSet": "assets",
"targetItemType": "computer",
"relationshipType": "related"
},
{
"targetId": "{DOCUMENT_ID}",
"targetDataSet": "documents",
"targetItemType": "document",
"relationshipType": "related"
},
{
"targetId": "{NOTE_ID}",
"targetDataSet": "notes",
"targetItemType": "note",
"relationshipType": null
}
],
"remove": []
}'The HTTP 200 response contains the added, removed and skipped counters. After the operation, retrieve the relationship collection and check that the result is as expected.
Releases - reading and removing relationships
Retrieve the relationship collection through its own route:
curl --request GET --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/relationships?page=1&pageSize=100" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"You can add one relationship without batch and then remove it with the current ETag:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-relationship-20260905133117" \
--data-raw '{"targetId":"{TICKET_ID}","targetDataSet":"tickets","targetItemType":"ticket","relationshipType":"related"}'
curl --request DELETE --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/relationships/tickets/{TICKET_ID}?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-release-relationship-delete-20260905133117"If the schema returns relationshipType: null for a target, omit the relationshipType query parameter from the delete route. You can also remove relationships through a partial release update:
curl --request PATCH --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-relationship-patch-20260905133117" \
--data-raw '{"relationshipsToRemove":[{"targetId":"{TICKET_ID}","targetDataSet":"tickets","targetItemType":"ticket","relationshipType":"related"}]}'After changing relationships, retrieve the release or relationship collection again. The ETag may change, so do not use the old ETag for the next action.
Releases - relationships with users
A release may have the agent, watcher and appUserRequester user relationships. The first identifies the person responsible for handling the release, the second an observer and the third the application user who submitted it. Do not add relationships that are not returned by the schema.
Assigning an agent:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/user-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-release-agent-20260905133117" \
--data-raw '{"targetId":"{USER_ID}","targetDataSet":"users","relationshipType":"agent"}'Adding a watcher through batch:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/user-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-release-watcher-20260905133117" \
--data-raw '{"add":[{"targetId":"{WATCHER_ID}","targetDataSet":"users","relationshipType":"watcher"}],"remove":[]}'Reading and removing relationships:
curl --request GET --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/user-relationships?page=1&pageSize=100" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"
curl --request DELETE --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/user-relationships/users/{USER_ID}?relationshipType=agent" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header 'If-Match: "{CURRENT_ETAG}"' \
--header "Idempotency-Key: public-api-release-agent-delete-20260905133117"You can also remove a watcher through user-relationships:batch with an empty add array and an entry in remove. Read the new ETag after each change.
Releases - files
Before a file operation, read the current release and its ETag. Uploading a file requires multipart/form-data:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-file-20260905133117" \
--form "[email protected];type=text/plain"File list and content download:
curl --request GET --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/files?page=1&pageSize=100" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET"
curl --request GET --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/files/{FILE_ID}/content" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--output release-evidence.txtA list item contains, among other values, id, name, fileName, contentType, size, relationshipType, isMain and downloadUrl. Treat downloadUrl as an API path, not as a public anonymous link.
You can attach an existing file to another release and then remove its relationship:
curl --request POST --url "$BASE_URL/api/v1/releases/{OTHER_RELEASE_ID}/files/{FILE_ID}?relationshipType=manual" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header 'If-Match: "{OTHER_RELEASE_CURRENT_ETAG}"' \
--header "Idempotency-Key: public-api-release-file-attach-20260905133117"
curl --request DELETE --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-file-delete-20260905133117"Before deletion, verify the release, fileId and current ETag. Obtain the size limit from the context. Do not load a file into memory before checking the limit.
Releases - pinning, spam and reopening
Workflow actions have separate endpoints. Do not replace them with an ordinary PATCH when the API provides a dedicated action. Each action requires the current ETag and its own idempotency key.
Pinning a release and marking it as spam:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/pin" \
--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-release-pin-20260905133117" \
--data-raw '{"pin":2}'
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/spam" \
--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-release-spam-on-20260905133117" \
--data-raw '{"isSpam":true}'Undo the spam mark by sending {"isSpam":false} to the same route. Reopen a release as follows:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/reopen" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header 'If-Match: "{CURRENT_ETAG}"' \
--header "Idempotency-Key: public-api-release-reopen-20260905133117"Actions may change the ETag. After each action, read the response and the current release before performing the next one.
Releases - rating and escalation
A rating can provide feedback and register an escalation request at the same time:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-rating-20260905133117" \
--data-raw '{
"rating": 4,
"feedback": "Rating sent by a Public API integration.",
"isEscalationRequested": true,
"escalationRequestReason": "The release requires second-line team analysis."
}'For rating alone, you need releases:rating:write; an escalation request also requires releases:escalation:write. After the operation, read the release again and verify the stored rating and escalation fields. Do not assume that HTTP 200 alone means that every value was saved.
Releases - approval and decision
An approval is a separate object that can be related to a release. Creating an approval requires the scopes for the approvals module and releases:approval:write:
curl --request POST --url "$BASE_URL/api/v1/approvals" \
--header "Content-Type: application/json" \
--header "X-Codenica-Client-Id: $CLIENT_ID" \
--header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
--header "Idempotency-Key: public-api-release-approval-create-20260905133117" \
--data-raw '{
"itemType": "approval",
"approverId": "{APPROVER_USER_ID}",
"attributes": {
"customId": "PUBLIC-API-RELEASE-20260905133117-APPROVAL",
"category": "Public API",
"description": "Release plan approval."
},
"relationships": [
{
"targetId": "{RELEASE_ID}",
"targetDataSet": "releases",
"targetItemType": "release"
}
]
}'After creation, read the approval through its endpoint and then save the decision through the release endpoint:
curl --request POST --url "$BASE_URL/api/v1/releases/{RELEASE_ID}/approvals/{APPROVAL_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-release-approval-decision-20260905133117" \
--data-raw '{"approve":true,"remark":"The release plan was approved through the Public API integration."}'After the decision, read the approval again and check its status or dateApproved. This confirms that the decision was saved, rather than only that the server accepted the request.
Releases - deleting a record
Deletion requires the current ETag and an idempotency key:
curl --request DELETE --url "$BASE_URL/api/v1/releases/{RELEASE_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-release-delete-20260905133117"After HTTP 200, perform a verification GET. The deleted release should return HTTP 404 with the release_not_found code or the equivalent specified in the contract. If the object has relationships or files, check the consequences in the schema and your database policy before deletion.
Releases - errors, limits and security
Successful responses return data in data, while technical information such as requestId and sometimes an ETag appears in meta. Errors use the Problem Details format with status, code, detail and requestId.
400- invalid body, parameter or field value;401- missing or invalid authentication;403- missing scope or database access;404- the release, file or relationship target does not exist or is not visible;409- data or idempotency conflict;412- stale ETag;413- upload or body is too large;428- ETag or Idempotency-Key is required;429- request limit exceeded;500or503- server error or temporary unavailability.
Respect the limits returned in the context for pageSize, batch items, files, relationships and rate limits. Read X-RateLimit-Limit, X-RateLimit-Remaining and, for 429, Retry-After. Use controlled retries with increasing delays.
For data-changing operations, always use a unique Idempotency-Key, the current If-Match where the endpoint requires it and the new ETag after a successful change. After a timeout, first reconstruct the result with GET or repeat the same request with the same key. Keep Client ID and Client Secret outside source code, do not write them to logs and do not send them in conversations or tickets.
Releases - integration sequence
- Determine the correct Cloud address or the actual address of the On-Premise installation.
- Create a separate key for the application and environment under Settings - API - API Keys.
- Grant only the scopes needed for releases and planned relationships.
- Send
GET /api/v1/contextand check the database, caller, scopes and limits. - Retrieve
GET /api/v1/releases/schemaand build the field mapping. - Retrieve the list with pagination, search or filters.
- Create a release with
POSTand a newIdempotency-Key. - Save the UUID and ETag.
- Before every change, read the current record and its ETag.
- Perform updates, relationships, file operations and workflow actions with the specific ETag and a new idempotency key.
- After each successful mutation, save the new ETag and read the result again.
- After
412, read the record, resolve the conflict and only then retry the operation. - For larger numbers of changes, use batch, but check the status of every item because a batch is not a transaction.
- For approvals, check the approval state after the decision.
- For deletion, use the current ETag and confirm HTTP 404 with a later GET.
This sequence lets you synchronise release planning and implementation without relying on accidental assumptions about fields, relationships or the installation address.
