Zmiany w Codenica API

Pracę ze zmianami przez Codenica API zaczynasz od utworzenia klucza w ustawieniach Codenica. Jeśli klucz nie został jeszcze utworzony, otwórz w nowej karcie Codenica API - wprowadzenie. Znajdziesz tam zasady tworzenia kluczy, przechowywania sekretu i wspólne reguły uwierzytelniania.

Techniczna nazwa modułu to changes, a typ pojedynczego obiektu to change. Zmiana służy do planowania i kontrolowania zaplanowanej modyfikacji usługi, infrastruktury albo konfiguracji. Oprócz podstawowych danych zawiera pola planowania, takie jak termin, ryzyko, wpływ, plan wdrożenia, plan wycofania i powód zmiany.

W dalszej części znajdziesz kompletny przebieg: sprawdzenie schematu i słowników, listy, filtrowanie, tworzenie, edycję z ETag, operacje batch, relacje, użytkowników, pliki, akcje workflow, akceptacje i usuwanie.

Przykłady wykorzystują prefix PUBLIC-API-CHANGE-20260905130127. W swojej integracji zastąp go własnym identyfikatorem, a adresy e-mail, identyfikatory i wartości pól dopasuj do danych w swojej bazie.


Zmiany - adres API i wybór instalacji

Wszystkie trasy dotyczące zmian zaczynają się od:

{BASE_URL}/api/v1/changes

W Codenica Cloud użyj publicznej domeny przypisanej do Twojej instalacji:

export BASE_URL="https://twoja-firma.codenica.com"

W domyślnej instalacji On-Premise adres lokalnie rejestrowany przez program Codenica Discovery to:

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

Jeżeli administrator opublikował instalację On-Premise pod firmową domeną, przez reverse proxy, z HTTPS albo na innym porcie, użyj dokładnego adresu przekazanego dla tej instalacji:

export BASE_URL="https://api.twoja-firma.example"

Nie używaj localhost, jeśli program integrujący działa na innym komputerze niż API. Nie przekazuj tenantId w body ani w query stringu. Właściwa baza danych jest wybierana na podstawie adresu hosta, z którym łączy się integracja.


Zmiany - klucz API i zakresy licencji

Klucz API utwórz w Codenica w miejscu Ustawienia - API - API Keys. Sekret jest pokazywany tylko raz, bezpośrednio po utworzeniu albo obróceniu klucza. Zapisz wtedy Client ID i Client Secret w bezpiecznym magazynie używanym przez integrację.

Public API jest dostępne dla licencji Plus i Enterprise. Licencja Plus pozwala utworzyć do 50 aktywnych kluczy, a Enterprise do 100. Starter nie udostępnia Public API. Dla każdej aplikacji i środowiska utwórz osobny klucz, aby można było niezależnie ograniczyć jego zakresy, obrócić sekret albo usunąć dostęp.

Do pracy ze zmianami wybierz tylko potrzebne uprawnienia. Integracja wyłącznie odczytująca może używać changes:read. Tworzenie akceptacji i decyzje akceptujących wymagają dodatkowo zakresów modułu approvals.


Zmiany - uwierzytelnianie i bezpieczne żądania

Każde żądanie publicznego API uwierzytelniaj dwoma nagłówkami klucza:

export CLIENT_ID="cna_twoj_client_id"
export CLIENT_SECRET="cns_twoj_client_secret"

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

Zewnętrzna integracja nie potrzebuje JWT administratora ani ciasteczek z panelu Codenica. Klucza nie umieszczaj w repozytorium, kodzie dostarczanym do przeglądarki, adresie URL, historii poleceń ani logach. Poza lokalnymi testami korzystaj z HTTPS.

W odpowiedzi zachowuj meta.requestId. Jest potrzebny do diagnozowania konkretnego żądania, ale nie zastępuje identyfikatora zmiany i nie powinien być używany jako sekret.


Zmiany - sprawdzenie kontekstu połączenia

Przed pierwszym zapisem pobierz kontekst. Dzięki temu sprawdzisz, czy adres prowadzi do właściwej bazy, a wybrany klucz ma potrzebne zakresy:

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

W odpowiedzi zweryfikuj:

  • data.apiVersion i data.contractVersion;
  • data.tenant.id oraz data.tenant.resolvedDomain;
  • data.caller.authentication równe api_key;
  • obecność changes w data.capabilities.resources;
  • zakresy przypisane do klucza;
  • limity stron, batch, plików i żądań.

Jeżeli kontekst wskazuje inną bazę albo nie zawiera wymaganego zakresu, zatrzymaj integrację i popraw adres lub klucz. Zakresów nie można nadać pojedynczym żądaniem.


Zmiany - zakresy uprawnień

Pełna obsługa zmian wymaga zakresów odpowiadających wykorzystywanym operacjom:

changes:read
changes:write
changes:delete
changes:schema
changes:stats
changes:relationships:read
changes:relationships:write
changes:users:read
changes:users:write
changes:files:read
changes:files:write
changes:technical:read
changes:technical:write
changes:pin:write
changes:spam:write
changes:reopen:write
changes:rating:write
changes:escalation:write
changes:approval:write

Do zwykłego odczytu wystarczy changes:read. Schemat i statystyki wymagają osobnych zakresów changes:schema oraz changes:stats. Odczyt relacji, użytkowników i plików wymaga odpowiednich zakresów :relationships:read, :users:read i :files:read. Operacje zapisu mają analogiczne zakresy :write.

Jeżeli integracja tworzy, odczytuje, zmienia albo usuwa obiekty akceptacji, dodaj:

approvals:read
approvals:write
approvals:delete
approvals:relationships:read
approvals:relationships:write
approvals:technical:read
approvals:technical:write

Relacje z innymi modułami wymagają też odczytu wskazywanego modułu, na przykład assets:read, documents:read, tickets:read, problems:read albo releases:read. Przyznawaj zakresy zgodnie z zasadą najmniejszych uprawnień.


Zmiany - schemat i pola planowania

Schemat pokazuje, które pola można odczytać i zapisać w konkretnej bazie. Pobierz go przed przygotowaniem body:

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

Dla każdego pola sprawdź między innymi readable, writable, required, technical, unique, maxLength oraz sposób automatycznego generowania. Schemat zwraca również dostępne cele relacji.

Minimalny zapis zmiany wymaga obecnie pól subject i requesterEmail. Pozostałe pola zależą od konfiguracji i procesu pracy:

Grupa
Przykładowe pola
Zastosowanie
Podstawowe
subject, requesterEmail, description, comments
opis i osoba zgłaszająca
Klasyfikacja
type, status, priority, impact, urgency, severity
sposób obsługi i ocena wpływu
Planowanie
datePlannedStart, datePlannedEnd, risk, impactInfo, rolloutPlan, backoutPlan, reasonForChange
termin, ryzyko i sposób wdrożenia
Integracja
source, externalNumber, referenceNumber, services, tags
powiązanie z innym systemem
Koszty
currency, estimatedCost, totalValue
wartości finansowe zmiany

Wartości statusu, priorytetu, typu, ryzyka i innych pól słownikowych pobieraj ze schematu albo z endpointu values. Daty przesyłaj w formacie ISO 8601, a liczby jako liczby JSON. Nie zakładaj, że słownik w dwóch bazach będzie identyczny.

{
  "datePlannedStart": "2030-01-15T09:00:00Z",
  "datePlannedEnd": "2030-01-15T17:00:00Z",
  "risk": "Medium",
  "impactInfo": "Planned impact assessment",
  "rolloutPlan": "Deploy and verify health checks.",
  "backoutPlan": "Restore the previous version if verification fails.",
  "reasonForChange": "The current platform version requires a controlled update."
}

Zmiany - podstawowe endpointy

Najczęściej używane trasy zmian są następujące:

  • GET /api/v1/changes - lista zmian;
  • GET /api/v1/changes/{id} - pojedyncza zmiana;
  • POST /api/v1/changes - utworzenie;
  • PATCH /api/v1/changes/{id} - częściowa edycja;
  • DELETE /api/v1/changes/{id} - usunięcie;
  • GET /api/v1/changes/schema - schemat pól i relacji;
  • GET /api/v1/changes/stats - statystyki;
  • GET /api/v1/changes/values - wartości używane w filtrach;
  • POST /api/v1/changes:batch - operacje create, update i delete.

Relacje, użytkownicy, pliki, akcje workflow i akceptacje mają osobne trasy. Dzięki temu integracja może otrzymać tylko te uprawnienia, które są rzeczywiście potrzebne.


Zmiany - listowanie i paginacja

Listę pobieraj stronicami. Nawet przy małej liczbie rekordów podaj jawnie numer strony i jej rozmiar:

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

Odpowiedź zawiera data.items oraz informacje page, pageSize, totalItems, totalPages i hasNextPage. Pobieraj następne strony, dopóki hasNextPage ma wartość true:

curl --request GET "$BASE_URL/api/v1/changes?page=2&pageSize=25&sort=dateUpdated&direction=desc" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Do synchronizacji najwygodniej sortować po dateUpdated i zapamiętywać ostatnio przetworzone rekordy. Nie ustawiaj pageSize wyższego niż limit zwrócony w kontekście.


Zmiany - wyszukiwanie, filtry i sortowanie

Parametry listy możesz łączyć. Poniższy przykład wyszukuje konkretny rekord, ogranicza go do typu change, ryzyka Medium i sortuje wynik według daty aktualizacji:

curl --get "$BASE_URL/api/v1/changes" \
  --data-urlencode "itemType=change" \
  --data-urlencode "customId=PUBLIC-API-CHANGE-20260905130127-SOURCE" \
  --data-urlencode "risk=Medium" \
  --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"

W codziennej synchronizacji przydatne są również parametry status, priority, subject, requesterEmail, source, externalNumber, referenceNumber, datePlannedStart, datePlannedEnd, createdAfter, createdBefore, updatedAfter i updatedBefore, o ile są dostępne w aktualnym kontrakcie.

Wartości tekstowe i daty koduj zgodnie z zasadami URL. Do wyszukiwania ogólnego użyj search, a do filtrowania konkretnego pola wykorzystaj parametr obsługiwany przez schemat danej instalacji. Nie zakładaj, że każda wartość słownika ma angielską nazwę.


Zmiany - wybór pól i dołączanie danych

Parametr fields pozwala ograniczyć odpowiedź do pól potrzebnych integracji. Parametr include dołącza dane powiązane:

curl --get "$BASE_URL/api/v1/changes/PUBLIC_CHANGE_UUID" \
  --data-urlencode "fields=subject,requesterEmail,status,priority,risk,datePlannedStart,datePlannedEnd" \
  --data-urlencode "include=files,relationships,users" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Przy odczycie całego modelu możesz użyć fields=*. Dołączanie plików, relacji i użytkowników wymaga odpowiednich zakresów odczytu. fields nie omija kontroli dostępu ani nie ujawnia pól technicznych, do których klucz nie ma uprawnień.

W odpowiedzi zwracaj uwagę na data.id, data.itemType, data.attributes i data.meta. Pola techniczne, takie jak pin albo isSpam, odczytuj, ale zmieniaj przez dedykowane akcje opisane dalej.


Zmiany - statystyki i wartości słownikowe

Statystyki pozwalają na przykład policzyć zmiany według ryzyka. Są operacją odczytową i nie modyfikują rekordów:

curl --get "$BASE_URL/api/v1/changes/stats" \
  --data-urlencode "field=risk" \
  --data-urlencode "limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Wartości pola przydatne do budowy filtrów pobierzesz osobno:

curl --get "$BASE_URL/api/v1/changes/values" \
  --data-urlencode "field=risk" \
  --data-urlencode "search=Medium" \
  --data-urlencode "limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Najpierw pobierz słownik, a dopiero potem wyślij wartość w body. Jest to szczególnie ważne dla risk, status, priority i type, ponieważ ich wartości mogą zależeć od konfiguracji językowej i ustawień konkretnej bazy.


Zmiany - tworzenie rekordu

Nową zmianę utworzysz przez POST /api/v1/changes. W body umieść techniczny typ change i zapisywalne pola w attributes. Poniższy przykład zawiera podstawowe dane, klasyfikację, informacje integracyjne oraz pełną grupę planowania:

export IDEMPOTENCY_KEY="public-api-change-create-20260905130127"

curl --request POST "$BASE_URL/api/v1/changes" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data-raw '{
    "itemType": "change",
    "attributes": {
      "customId": "PUBLIC-API-CHANGE-20260905130127-SOURCE",
      "subject": "PUBLIC-API-CHANGE-20260905130127 integration request",
      "requesterEmail": "[email protected]",
      "description": "Created by the Public API Changes flow",
      "comments": "ITSM integration change",
      "source": "Public API",
      "type": "Standard",
      "status": "Closed",
      "priority": "High",
      "impact": "Medium",
      "urgency": "High",
      "severity": "High",
      "services": "Codenica Public API",
      "tags": "public-api,change",
      "externalNumber": "EXT-PUBLIC-API-CHANGE-20260905130127",
      "referenceNumber": "REF-PUBLIC-API-CHANGE-20260905130127",
      "currency": "PLN",
      "estimatedCost": 12.5,
      "totalValue": 12.5,
      "datePlannedStart": "2030-01-15T09:00:00Z",
      "datePlannedEnd": "2030-01-15T17:00:00Z",
      "risk": "Medium",
      "impactInfo": "Planned impact assessment for the integration request",
      "rolloutPlan": "Deploy the approved change and verify health checks.",
      "backoutPlan": "Restore the previous release if verification fails.",
      "reasonForChange": "The current platform version requires a controlled update."
    }
  }'

Poprawne utworzenie zwraca 201 Created. Zapisz data.id, wartość ETag z nagłówka HTTP oraz data.meta.etag. Pole customValues jest opcjonalne i służy do wartości niestandardowych tylko wtedy, gdy integracja zna konfigurację tych pól.

Jeśli baza wymaga innych wartości słownikowych, nie kopiuj powyższych nazw bez sprawdzenia schematu i endpointu values.


Zmiany - bezpieczne ponowienie przez Idempotency-Key

Każdą operację zmieniającą dane wykonuj z unikalnym Idempotency-Key. Jeżeli odpowiedź zniknie z powodu przerwania sieci, możesz powtórzyć dokładnie to samo żądanie z tym samym kluczem:

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

Idempotencja sprawia, że ponowienie identycznego żądania odtworzy wynik tej samej operacji zamiast utworzyć drugą zmianę. Ten sam klucz nie może służyć do innego body. Dla nowej zmiany, nowej edycji, relacji, pliku i akcji wygeneruj nowy klucz.

Idempotencja nie zastępuje ETag. Przy operacji, która wymaga kontroli wersji, przekazuj jednocześnie aktualny If-Match.


Zmiany - odczyt rekordu i ETag

Po utworzeniu albo znalezieniu identyfikatora pobierz pojedynczą zmianę:

export CHANGE_ID="PUBLIC_CHANGE_UUID"

curl --get "$BASE_URL/api/v1/changes/$CHANGE_ID" \
  --data-urlencode "fields=*" \
  --data-urlencode "include=files,relationships,users" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

ETag otrzymasz w nagłówku HTTP ETag oraz w data.meta.etag. Traktuj tę wartość jako wersję konkretnej zmiany. Zapisz ją przed każdą kolejną operacją modyfikującą dane.

ETag może zmienić się po edycji pól, zmianie relacji, przypisaniu użytkownika, uploadzie lub usunięciu pliku oraz wykonaniu akcji workflow. Po każdej udanej mutacji odczytaj nowy stan albo pobierz nowy ETag z odpowiedzi.


Zmiany - edycja z If-Match

Do częściowej edycji użyj PATCH. Przekazuj tylko pola, które mają się zmienić, bieżący ETag i nowy klucz idempotencji:

curl --request PATCH "$BASE_URL/api/v1/changes/$CHANGE_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-change-update-20260905130127" \
  --data-raw '{
    "attributes": {
      "description": "Updated change PUBLIC-API-CHANGE-20260905130127",
      "status": "Closed",
      "priority": "High",
      "risk": "Low",
      "impactInfo": "Updated impact assessment",
      "rolloutPlan": "Run the revised deployment plan and verify the service.",
      "backoutPlan": "Restore the previous release if the revised change fails.",
      "reasonForChange": "Updated implementation rationale."
    }
  }'

Poprawny ETag daje 200 OK i nową wersję rekordu. Pola techniczne, takie jak pin i isSpam, zmieniaj przez dedykowane endpointy. Nie próbuj zmieniać ich zwykłym PATCH, jeśli schemat wskazuje, że są tylko do odczytu.

Daty planowania pozostają zwykłymi polami zmiany, dlatego aktualizujesz je w attributes. Przed zapisem sprawdź, czy są oznaczone jako writable.


Zmiany - nieaktualny lub brakujący ETag

Jeśli inny proces zmienił rekord od czasu Twojego odczytu, stary ETag nie pozwoli go nadpisać. Dla nieaktualnej wartości API zwraca 412 Precondition Failed i kod if_match_failed:

{
  "status": 412,
  "code": "if_match_failed"
}

Brak nagłówka If-Match przy wymaganej mutacji zwraca 428 Precondition Required z kodem if_match_required:

{
  "status": 428,
  "code": "if_match_required"
}

Po obu odpowiedziach pobierz zmianę ponownie, sprawdź nowy stan i zdecyduj, czy Twoja aktualizacja nadal jest potrzebna. Następnie wyślij ją z nowym ETag i nowym kluczem idempotencji. Nie wyłączaj kontroli współbieżności po stronie integracji.


Zmiany - operacje batch

Operacja batch pozwala połączyć wiele niezależnych operacji w jednym żądaniu. Poniższy przykład tworzy dwie zmiany:

curl --request POST "$BASE_URL/api/v1/changes: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-change-batch-create-20260905130127" \
  --data-raw '{
    "items": [
      {
        "operation": "create",
        "create": {
          "itemType": "change",
          "attributes": {
            "customId": "PUBLIC-API-CHANGE-20260905130127-BATCH-A",
            "subject": "Batch change A",
            "requesterEmail": "[email protected]",
            "description": "Batch change A",
            "source": "Public API",
            "type": "Standard",
            "status": "Open",
            "priority": "Medium",
            "risk": "Low",
            "datePlannedStart": "2030-02-10T09:00:00Z",
            "datePlannedEnd": "2030-02-10T12:00:00Z"
          }
        }
      },
      {
        "operation": "create",
        "create": {
          "itemType": "change",
          "attributes": {
            "customId": "PUBLIC-API-CHANGE-20260905130127-BATCH-B",
            "subject": "Batch change B",
            "requesterEmail": "[email protected]",
            "description": "Batch change B",
            "source": "Public API",
            "type": "Standard",
            "status": "Open",
            "priority": "Low",
            "risk": "High",
            "datePlannedStart": "2030-02-11T09:00:00Z",
            "datePlannedEnd": "2030-02-11T12:00:00Z"
          }
        }
      }
    ]
  }'

Odpowiedź zawiera items, status każdej operacji oraz liczniki succeeded i failed. Przetwórz każdą pozycję osobno. Batch nie jest transakcją all-or-nothing, więc błąd jednego elementu nie musi cofnąć pozostałych.

Aktualizacja i usunięcie wymagają ETag-u każdego rekordu. Body dla batch zawierającego zmianę i usunięcie może wyglądać tak:

{
  "items": [
    {
      "operation": "update",
      "id": "CHANGE_A_UUID",
      "ifMatch": "\"CHANGE_A_ETAG\"",
      "update": {
        "attributes": {
          "description": "Batch update A"
        }
      }
    },
    {
      "operation": "delete",
      "id": "CHANGE_B_UUID",
      "ifMatch": "\"CHANGE_B_ETAG\""
    }
  ]
}

Jeden klucz idempotencji identyfikuje całe żądanie batch, a nie poszczególne elementy. Po wyniku zapisz identyfikatory i ETag-i tylko tych rekordów, które zostały poprawnie utworzone albo zmienione.


Zmiany - relacje z obiektami

Lista możliwych celów relacji znajduje się w odpowiedzi changes/schema. W zależności od dostępu i danych możesz łączyć zmianę między innymi z zasobami, dokumentami, innymi zmianami, zgłoszeniami, problemami i wydaniami. Każdy cel musi być widoczny dla klucza, a targetItemType musi odpowiadać rzeczywistemu typowi obiektu.

Dodanie pojedynczej relacji:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_ID/relationships" \
  --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-change-relationship-asset-20260905130127" \
  --data-raw '{
    "targetId": "ASSET_UUID",
    "targetDataSet": "assets",
    "targetItemType": "computer",
    "relationshipType": "related"
  }'

Pojedyncze dodanie zwraca 201 Created. Kilka relacji możesz dodać w jednym żądaniu:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_ID/relationships: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 'If-Match: "CURRENT_ETAG"' \
  --header "Idempotency-Key: public-api-change-relationship-batch-20260905130127" \
  --data-raw '{
    "add": [
      {
        "targetId": "ASSET_UUID",
        "targetDataSet": "assets",
        "targetItemType": "computer",
        "relationshipType": "related"
      },
      {
        "targetId": "DOCUMENT_UUID",
        "targetDataSet": "documents",
        "targetItemType": "document",
        "relationshipType": "related"
      }
    ],
    "remove": []
  }'

Odczyt relacji:

curl --request GET "$BASE_URL/api/v1/changes/$CHANGE_ID/relationships?page=1&pageSize=100" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Batch zwraca liczniki added, removed i skipped. Każda zmiana relacji zmienia ETag źródła, dlatego przed następną mutacją odczytaj nową wartość.


Zmiany - usuwanie relacji

Relację usuwaj z aktualnym ETag-em źródłowej zmiany. W ścieżce podaj zbiór danych celu i jego identyfikator:

curl --request DELETE "$BASE_URL/api/v1/changes/$CHANGE_ID/relationships/changes/$TARGET_CHANGE_ID?relationshipType=related" \
  --header "Accept: application/json, application/problem+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-change-relationship-delete-20260905130127"

Dla zasobu użyj analogicznie relationships/assets/{TARGET_ID}, a dla dokumentu relationships/documents/{TARGET_ID}. Parametr relationshipType powinien odpowiadać typowi relacji, który został zapisany.

Relacje można też usuwać w ramach częściowej edycji:

curl --request PATCH "$BASE_URL/api/v1/changes/$CHANGE_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-change-relationship-patch-20260905130127" \
  --data-raw '{
    "relationshipsToRemove": [
      {
        "targetId": "TARGET_UUID",
        "targetDataSet": "changes",
        "targetItemType": "change",
        "relationshipType": "related"
      }
    ]
  }'

Po usunięciu relacji pobierz listę ponownie i upewnij się, że usunięto właściwy cel. Usunięcie relacji nie usuwa rekordu, który był jej celem.


Zmiany - relacje z użytkownikami

Relacje z użytkownikami są osobnym mechanizmem. Zmiana obsługuje trzy role: agent dla osoby wykonującej pracę, watcher dla obserwatora oraz appUserRequester dla użytkownika zgłaszającego. Ten obiekt nie ma relacji clientRequester.

Przypisanie agenta:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_ID/user-relationships" \
  --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-change-agent-20260905130127" \
  --data-raw '{
    "targetId": "USER_UUID",
    "targetDataSet": "users",
    "relationshipType": "agent"
  }'

Dodanie obserwatora w batch:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_ID/user-relationships: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 'If-Match: "CURRENT_ETAG"' \
  --header "Idempotency-Key: public-api-change-watcher-20260905130127" \
  --data-raw '{
    "add": [
      {
        "targetId": "WATCHER_USER_UUID",
        "targetDataSet": "users",
        "relationshipType": "watcher"
      }
    ],
    "remove": []
  }'

Listę relacji pobierzesz tak:

curl --request GET "$BASE_URL/api/v1/changes/$CHANGE_ID/user-relationships?page=1&pageSize=100" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Usunięcie przypisania wskazuje typ relacji w parametrze:

curl --request DELETE "$BASE_URL/api/v1/changes/$CHANGE_ID/user-relationships/users/$USER_ID?relationshipType=appUserRequester" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "CURRENT_ETAG"' \
  --header "Idempotency-Key: public-api-change-app-user-requester-delete-20260905130127"

Agenta, obserwatora albo użytkownika zgłaszającego możesz usunąć tą samą trasą, zmieniając relationshipType. Obserwatora można także usunąć przez batch z pustym add i wpisem w remove.


Zmiany - pliki

Plik przesyłany do zmiany ma własny identyfikator i metadane. Upload wymaga aktualnego ETag-u, nowego klucza idempotencji i żądania multipart/form-data:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_ID/files?relationshipType=documentation" \
  --header "Accept: application/json, application/problem+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-change-file-one-20260905130127" \
  --form "[email protected];type=text/plain"

Udany upload zwraca 201 Created z identyfikatorem i metadanymi pliku. Listę plików pobierzesz tak:

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

Treść pobieraj jako dane binarne i zapisz do pliku:

export FILE_ID="FILE_UUID"

curl --request GET "$BASE_URL/api/v1/changes/$CHANGE_ID/files/$FILE_ID/content" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --output downloaded-change-file.bin

Istniejący plik możesz dołączyć do innej zmiany. ETag dotyczy wtedy zmiany docelowej:

curl --request POST "$BASE_URL/api/v1/changes/OTHER_CHANGE_UUID/files/$FILE_ID?relationshipType=manual" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "OTHER_CHANGE_ETAG"' \
  --header "Idempotency-Key: public-api-change-file-attach-20260905130127"

Usunięcie pliku wykonuje się przez DELETE /api/v1/changes/{id}/files/{fileId}. Operacja zwraca 200 OK, jeśli zakończyła się poprawnie. Po uploadzie, podpięciu i usunięciu odśwież ETag zmiany oraz listę plików.

curl --request DELETE "$BASE_URL/api/v1/changes/$CHANGE_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-change-file-delete-20260905130127"

Zmiany - przypięcie, spam i ponowne otwarcie

Przypięcie, oznaczenie spamu i ponowne otwarcie są osobnymi akcjami. Każda z nich wymaga aktualnego If-Match oraz nowego Idempotency-Key.

Przypięcie zmiany:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-pin-20260905130127" \
  --data-raw '{"pin":2}'

Aby cofnąć przypięcie, użyj tej samej trasy z wartością null, jeśli pozwala na to schemat i uprawnienia:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-unpin-20260905130127" \
  --data-raw '{"pin":null}'

Oznaczenie jako spam i cofnięcie oznaczenia:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-spam-20260905130127" \
  --data-raw '{"isSpam":true}'

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-spam-undo-20260905130127" \
  --data-raw '{"isSpam":false}'

Ponowne otwarcie zamkniętej zmiany:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-reopen-20260905130127"

Po każdej akcji pobierz zmianę ponownie i zapisz nowy ETag. Potrzebne zakresy to odpowiednio changes:pin:write, changes:spam:write i changes:reopen:write.


Zmiany - ocena i eskalacja

Ocena jest zapisywana przez osobny endpoint. Możesz dołączyć do niej prośbę o eskalację:

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-rating-20260905130127" \
  --data-raw '{
    "rating": 4,
    "feedback": "Rating from the integration",
    "isEscalationRequested": true,
    "escalationRequestReason": "Public API escalation request"
  }'

Zakres changes:rating:write jest potrzebny do zapisania oceny, a changes:escalation:write do dołączenia żądania eskalacji. Ocena ma wartość zgodną z zakresem przyjętym przez API. Po zapisaniu odczytaj pola techniczne, między innymi rating, feedback, daty oceny i escalationRequestReason.

Jeśli nie chcesz eskalacji, pomiń pola isEscalationRequested i escalationRequestReason. Nie wysyłaj żądania eskalacji bez opisu powodu.


Zmiany - akceptacja i decyzja

Akceptację możesz utworzyć jako osobny obiekt approval i połączyć ją ze zmianą relacją. Do utworzenia potrzebujesz zakresów modułu akceptacji oraz identyfikatora osoby, która ma podjąć decyzję:

export APPROVER_ID="USER_UUID"

curl --request POST "$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-change-approval-create-20260905130127" \
  --data-raw '{
    "itemType": "approval",
    "approverId": "USER_UUID",
    "attributes": {
      "customId": "PUBLIC-API-CHANGE-20260905130127-APPROVAL",
      "category": "Public API",
      "description": "Changes approval"
    },
    "relationships": [
      {
        "targetId": "CHANGE_UUID",
        "targetDataSet": "changes",
        "targetItemType": "change"
      }
    ]
  }'

Osoba wskazana w approverId może podjąć decyzję trasą zmiany. APPROVAL_ID oznacza identyfikator akceptacji, a nie użytkownika:

export APPROVAL_ID="APPROVAL_UUID"

curl --request POST "$BASE_URL/api/v1/changes/$CHANGE_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-change-approval-decision-20260905130127" \
  --data-raw '{
    "approve": true,
    "remark": "Approved through Changes Public API."
  }'

Odrzucenie wykonuje się z approve równym false i własnym komentarzem. Po decyzji odczytaj akceptację i sprawdź jej status oraz datę decyzji. Następnie odśwież zmianę, ponieważ decyzja może zmienić jej ETag i stan procesu.


Zmiany - usuwanie rekordu

Przed usunięciem pobierz zmianę ponownie i użyj aktualnego ETag-u:

curl --request DELETE "$BASE_URL/api/v1/changes/$CHANGE_ID" \
  --header "Accept: application/json, application/problem+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-change-delete-20260905130127"

Po 200 OK wykonaj GET tego samego UUID-u. Oczekuj 404 z kodem change_not_found albo odpowiednim kodem rekordu. Jeżeli chcesz sprawdzić synchronizację, wykonaj listę z filtrem po customId i upewnij się, że totalItems wynosi zero.

Usunięcie zmiany nie powinno być używane jako sposób archiwizacji historii. Przed operacją produkcyjną sprawdź politykę przechowywania danych, relacje i wymagania audytowe. Jeśli rekord powinien pozostać w historii, zmień jego status zamiast go usuwać.


Zmiany - odpowiedzi błędów, limity i bezpieczeństwo

Błędy zwracane są w formacie application/problem+json. Przykładowa odpowiedź:

{
  "type": "https://docs.codenica.com/errors/change_not_found",
  "title": "Change not found.",
  "status": 404,
  "detail": "The change does not exist or is outside the caller's access scope.",
  "instance": "/api/v1/changes/PUBLIC_CHANGE_UUID",
  "code": "change_not_found",
  "requestId": "request-id-from-response"
}

W logice integracji używaj przede wszystkim status i code. Pole detail jest informacją dla człowieka i może zmienić treść.

HTTP
Znaczenie
401
brak albo nieprawidłowe uwierzytelnienie
403
brak wymaganego zakresu albo uprawnień użytkownika
404
rekord, plik albo cel relacji nie istnieje lub jest niewidoczny
409
konflikt danych albo idempotencji
412
nieaktualny ETag
422
body albo wartości pól są niepoprawne
428
brakuje If-Match albo Idempotency-Key
429
przekroczono limit żądań

Odczytuj nagłówki X-RateLimit-Limit i X-RateLimit-Remaining. Po 429 zastosuj backoff i respektuj ewentualny Retry-After. Nie omijaj limitu przez tworzenie wielu kluczy ani zwiększanie równoległości żądań. W logach zapisuj metodę, endpoint, status i requestId, ale nigdy Client Secret ani pełnych nagłówków uwierzytelniających.


Zmiany - kolejność pracy integracji

  1. Ustaw BASE_URL dla właściwej instalacji Cloud albo On-Premise.
  2. Utwórz osobny klucz w Ustawienia - API - API Keys i wybierz minimalne zakresy.
  3. Umieść Client ID i Client Secret w bezpiecznym magazynie.
  4. Wyślij GET /api/v1/context i sprawdź bazę, zakresy oraz limity.
  5. Pobierz GET /api/v1/changes/schema oraz wartości pól używanych przez integrację.
  6. Pobierz listę zmian albo utwórz nową przez POST z unikalnym Idempotency-Key.
  7. Zapisz UUID zmiany i jej ETag.
  8. Przed każdą mutacją odśwież ETag i użyj nowego klucza idempotencji.
  9. Dodawaj relacje, użytkowników i pliki dopiero po sprawdzeniu katalogu celów w schemacie.
  10. Akcje workflow i decyzje akceptacji wykonuj osobno, a po każdej odczytaj nowy stan.
  11. Przy 412 pobierz rekord, rozstrzygnij konflikt i świadomie ponów operację.
  12. Przy batch sprawdź wynik każdej pozycji, ponieważ częściowy błąd nie musi cofnąć sukcesów.
  13. Obsłuż 429, zapisuj requestId bez sekretów i usuwaj klucz, gdy integracja przestaje być używana.

Tak przygotowany przepływ pozwala synchronizować planowane zmiany z innym systemem bez opierania integracji na wewnętrznej strukturze bazy. Jeśli zmieni się konfiguracja pól, adres instalacji lub zakresy klucza, ponownie odczytaj kontekst i schemat.