Akceptacje w Codenica API

Pracę z Akceptacjami 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 wspólne zasady tworzenia klucza, przechowywania sekretu i uwierzytelniania.

Techniczna nazwa obiektu to approval, a nazwa jego zbioru w API to approvals. Akceptacja przechowuje wniosek, osobę odpowiedzialną za decyzję, dane opisowe i powiązania z obiektami procesu. Może również mieć pliki oraz przypięcie.

Najważniejsza różnica względem zwykłej edycji polega na tym, że wyniku decyzji nie zapisuje się bezpośrednio w polu status. Akceptację zatwierdza się albo odrzuca przez dedykowany endpoint decyzji. Dzięki temu API sprawdza, czy decyzję wykonuje właściwy approver i czy rekord nie został zmieniony po ostatnim odczycie.

Przykłady używają identyfikatora PUBLIC-API-APPROVAL-20260906060644. Zastąp go własnym identyfikatorem pochodzącym z systemu integrującego, a UUID-y i wartości pól dopasuj do danych w swojej bazie.


Akceptacje - adres API i wybór instalacji

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

{BASE_URL}/api/v1/approvals

BASE_URL oznacza adres serwera Codenica bez końcówki /api/v1. W wersji Cloud użyj rzeczywistej domeny przypisanej do firmy:

export BASE_URL="https://{rzeczywista-domena-firmy}"

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

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

Jeżeli administrator udostępnił instalację 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://{rzeczywisty-adres-instalacji}"

Nie używaj localhost, jeżeli program integrujący działa na innym komputerze niż API. Właściwa baza danych jest wybierana na podstawie hosta żądania. Nie przekazuj tenantId w body, query stringu ani dodatkowym nagłówku.


Akceptacje - zakresy klucza API

Klucz używany do pracy z Akceptacjami powinien mieć tylko zakresy potrzebne przez konkretną integrację. Pełny zestaw zakresów modułu wygląda tak:

approvals:read
approvals:write
approvals:delete
approvals:schema
approvals:stats
approvals:relationships:read
approvals:relationships:write
approvals:users:read
approvals:files:read
approvals:files:write
approvals:technical:read
approvals:technical:write
approvals:pin:write
approvals:decision:write
users:read

Do odczytu list i rekordów wybierz approvals:read. Tworzenie i edycja wymagają approvals:write, usuwanie wymaga approvals:delete. Zakresy relacji, plików, statystyk, pól technicznych, pinowania i decyzji dodaj tylko wtedy, gdy integracja będzie wykonywać te operacje.

Jeśli integracja wyszukuje cele relacji, potrzebuje również odpowiednich zakresów odczytu dla zbiorów, z których wybiera cele, na przykład notes:read, worktasks:read, requesteditems:read, tickets:read, changes:read, problems:read albo releases:read. Zakres klucza nie zastępuje uprawnień użytkownika.


Akceptacje - uwierzytelnianie

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

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

curl --request GET --url "$BASE_URL/api/v1/approvals?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. Sekret przechowuj w magazynie sekretów po stronie serwera. Nie umieszczaj go w kodzie dostarczanym do przeglądarki, repozytorium, adresie URL, historii poleceń ani logach. Poza lokalnymi testami korzystaj z HTTPS.

W odpowiedziach zapisuj meta.requestId. Ten identyfikator pomaga znaleźć konkretne żądanie w logach, ale nie zastępuje UUID Akceptacji i nie jest sekretem.


Akceptacje - sprawdzenie kontekstu połączenia

Przed pierwszym zapisem pobierz kontekst. Sprawdzisz w ten sposób, czy adres prowadzi do właściwej bazy danych, a klucz ma wymagane zakresy i limity:

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"

W odpowiedzi zweryfikuj data.apiVersion, data.contractVersion, dane data.tenant, wartość data.caller.authentication równą api_key, obecność approvals w data.capabilities.resources, zakresy klucza i limity.

Jeżeli kontekst wskazuje inną firmę albo nie zawiera potrzebnego zakresu, popraw adres lub utwórz klucz z właściwymi uprawnieniami. Nie próbuj kierować żądania do innej bazy przez przesłanie obcego tenantId.


Akceptacje - schema i cele relacji

Schema jest źródłem informacji o aktualnych polach, ich typach, zapisywalności i dopuszczalnych celach relacji:

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

W odpowiedzi znajdziesz między innymi data.itemType, data.fields i data.relationshipTargets. Dla tego modułu itemType ma wartość approval. Dla każdego pola sprawdź readable, writable, required, technical, unique, maxLength i hasAutoGeneration.

Przykładowy fragment schematu:

{
  "data": {
    "itemType": "approval",
    "fields": [
      { "name": "description", "type": "string", "writable": true },
      { "name": "level", "type": "string", "writable": true },
      { "name": "status", "type": "string", "writable": false },
      { "name": "pin", "type": "integer", "writable": false }
    ],
    "relationshipTargets": [
      { "targetDataSet": "notes", "targetItemType": "note" },
      { "targetDataSet": "tickets", "targetItemType": "ticket" }
    ]
  }
}

Nie buduj mapowania wyłącznie na podstawie przykładu. Przed uruchomieniem integracji pobierz schema dla właściwej bazy i używaj tylko zwróconych pól oraz celów.


Akceptacje - pola biznesowe i systemowe

Najważniejsze pola Akceptacji to:

Pole
Typ
Zastosowanie
customId
string
Identyfikator po stronie integracji
location, department
string
Lokalizacja i dział odpowiedzialny za proces
tag, link
string
Oznaczenia i odnośnik do źródła
info, description
string
Informacja pomocnicza i opis wniosku
level, category
string
Poziom akceptacji i kategoria procesu
status, dateApproved, dateRejected
read-only
Stan oraz daty wyniku decyzji
remark, pin
systemowe
Komentarz decyzji i poziom przypięcia

id, itemType, creator, updater, dateCreated, dateUpdated, importId, importSource i dateImported są ustalane przez system albo przeznaczone do odczytu technicznego. Nie wysyłaj ich w attributes.

creator
updater
dateCreated
dateUpdated
importId
importSource
dateImported

Akceptacje - dostępne endpointy

Najważniejsze trasy modułu approvals to:

GET    /api/v1/approvals
POST   /api/v1/approvals
GET    /api/v1/approvals/{APPROVAL_ID}
PATCH  /api/v1/approvals/{APPROVAL_ID}
DELETE /api/v1/approvals/{APPROVAL_ID}
GET    /api/v1/approvals/schema
GET    /api/v1/approvals/stats
GET    /api/v1/approvals/values
POST   /api/v1/approvals:batch
GET    /api/v1/approvals/{APPROVAL_ID}/relationships
POST   /api/v1/approvals/{APPROVAL_ID}/relationships
POST   /api/v1/approvals/{APPROVAL_ID}/relationships:batch
DELETE /api/v1/approvals/{APPROVAL_ID}/relationships/{DATASET}/{TARGET_ID}
GET    /api/v1/approvals/{APPROVAL_ID}/user-relationships
GET    /api/v1/approvals/{APPROVAL_ID}/files
POST   /api/v1/approvals/{APPROVAL_ID}/files
POST   /api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}
DELETE /api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}
GET    /api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}/content
POST   /api/v1/approvals/{APPROVAL_ID}/pin
POST   /api/v1/approvals/{APPROVAL_ID}/decision

Odczyty wymagają zakresów read, a poszczególne mutacje dodatkowych zakresów zgodnych z tabelą uprawnień. Każde żądanie zmieniające dane wymaga również Idempotency-Key, a operacje na istniejącym rekordzie dodatkowo aktualnego If-Match.


Akceptacje - listowanie i paginacja

Listę Akceptacji pobieraj stronami:

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

Odpowiedź ma kolekcję data.items oraz informacje o stronie:

{
  "data": {
    "items": [
      {
        "id": "approval-uuid",
        "itemType": "approval",
        "attributes": {
          "customId": "ERP-APPROVAL-2026-0042",
          "category": "Procurement",
          "status": "Open",
          "level": "Supervisor"
        },
        "meta": {
          "etag": "\"etag-value\""
        }
      }
    ],
    "page": 1,
    "pageSize": 25,
    "totalItems": 1,
    "totalPages": 1,
    "hasNextPage": false
  },
  "meta": {
    "requestId": "request-id"
  }
}

Przechodź do następnej strony na podstawie hasNextPage. Maksymalny rozmiar strony odczytaj z data.capabilities.limits.maxPageSize, zamiast zakładać go na stałe.


Akceptacje - wyszukiwanie i filtry

Do wyszukiwania tekstowego użyj parametru search. Do synchronizacji najlepiej stosować stabilny customId albo UUID:

curl --silent --show-error -G \
  --data-urlencode "search=zakup" \
  --data-urlencode "page=1" \
  --data-urlencode "pageSize=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals"

Proste filtry mogą korzystać z nazw pól:

curl --silent --show-error -G \
  --data-urlencode "status=Open" \
  --data-urlencode "category=Procurement" \
  --data-urlencode "customId=ERP-APPROVAL-2026-0042" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals"

Filtr strukturalny ma postać field:operator:value:

category:eq:Procurement
level:ne:Assistant
description:contains:monitor
customId:startswith:ERP-
link:notempty:

Obsługiwane operatory to między innymi eq, ne, gt, gte, lt, lte, contains, startswith, endswith i notempty. Wartość filtra URL-encode’uj, zwłaszcza gdy zawiera spację, dwukropek lub znak specjalny.


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

Jeżeli potrzebujesz tylko części odpowiedzi, użyj fields. Dołączanie plików, relacji i użytkowników wykonuje się przez include:

curl --silent --show-error -G \
  --data-urlencode "fields=id,itemType,customId,location,department,level,category,status" \
  --data-urlencode "include=files,relationships,users" \
  --data-urlencode "ids={APPROVAL_ID}" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals"

Dostępne wartości include to files, relationships i users. Każda z nich może wymagać osobnego zakresu. fields=* nie omija uprawnień do pól technicznych i nie zmienia reguł dostępu.

Do filtrowania możesz wykorzystać także createdAfter, createdBefore, updatedAfter, updatedBefore, sort i direction. Nazwy pól w fields, sort i filter sprawdzaj względem aktualnego schema.


Akceptacje - statystyki i wartości pól

Endpoint stats pomaga sprawdzić rozkład danych, a values zwraca wartości przydatne do budowy filtrów:

curl --silent --show-error -G \
  --data-urlencode "field=category" \
  --data-urlencode "limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/stats"

curl --silent --show-error -G \
  --data-urlencode "field=level" \
  --data-urlencode "search=super" \
  --data-urlencode "limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/values"

Oba żądania są odczytowe i nie zmieniają Akceptacji. Pole musi być dopuszczone w schema, a zakres wartości zależy od rekordów widocznych dla użytkownika.


Akceptacje - minimalne utworzenie

Minimalny praktyczny rekord zawiera typ, identyfikator po stronie integracji, kategorię, opis oraz użytkownika, który ma wykonać decyzję:

{
  "itemType": "approval",
  "attributes": {
    "customId": "ERP-APPROVAL-0001",
    "category": "Procurement",
    "description": "Akceptacja zakupu monitora"
  },
  "approverId": "{USER_ID_APPROVERA}"
}

Prześlij go jako JSON:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: erp-approval-create-0001" \
  --data-binary @approval.json \
  "$BASE_URL/api/v1/approvals"

approverId wskazuje aktywnego użytkownika Codenica, który ma podjąć decyzję. Nie jest to identyfikator klienta w kartotece ani dowolny adres e-mail.


Akceptacje - kompletne utworzenie z przykładem danych

Poniższy przykład pokazuje rekord z opisem procesu, oznaczeniami, poziomem akceptacji i regułą wartości technicznej:

{
  "itemType": "approval",
  "attributes": {
    "customId": "PUBLIC-API-APPROVAL-20260906060644-SOURCE",
    "location": "Warsaw",
    "department": "IT",
    "tag": "public-api,approvals,PUBLIC-API-APPROVAL-20260906060644",
    "link": "https://codenica.com",
    "info": "Approval request created by the Public API complete flow.",
    "level": "Supervisor",
    "category": "Procurement",
    "description": "Approval created through the Codenica Public API."
  },
  "approverId": "{USER_ID_APPROVERA}",
  "customValues": [
    {
      "name": "description",
      "valuePattern": "[approval-test] PUBLIC-API-APPROVAL-20260906060644"
    }
  ]
}

approverId wymaga approvals:technical:write. customValues jest opcjonalne i również wymaga zakresu technicznego. Używaj go tylko dla pól dopuszczonych przez schema.

W praktycznej integracji wybierz approvera zgodnie z procesem firmy. Użytkownik, który tworzy rekord, zostaje requesterem.


Akceptacje - odpowiedź create i klucz idempotencji

Udane utworzenie zwraca 201 Created, identyfikator rekordu i początkowy ETag. Zapisz oba identyfikatory po stronie integracji:

{
  "data": {
    "id": "{APPROVAL_ID}",
    "itemType": "approval",
    "attributes": {
      "customId": "PUBLIC-API-APPROVAL-20260906060644-SOURCE",
      "level": "Supervisor",
      "category": "Procurement",
      "status": "Open"
    },
    "meta": {
      "customId": "PUBLIC-API-APPROVAL-20260906060644-SOURCE",
      "etag": "\"{ETAG_AFTER_CREATE}\""
    }
  },
  "meta": {
    "requestId": "{REQUEST_ID}",
    "etag": "\"{ETAG_AFTER_CREATE}\""
  }
}

Każde mutujące żądanie musi mieć własny Idempotency-Key. Jeżeli klient nie wie, czy pierwsze żądanie dotarło do serwera, wyślij ponownie identyczne body z identycznym kluczem:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: public-api-approval-source-create-20260906060644" \
  --data-binary @approval.json \
  "$BASE_URL/api/v1/approvals"

Powtórzenie tego samego żądania nie tworzy drugiej Akceptacji. Zmienione body z tym samym kluczem zostanie odrzucone, ponieważ jeden klucz nie może oznaczać dwóch różnych operacji.


Akceptacje - odczyt pojedynczego rekordu i ETag

Po utworzeniu albo przed każdą kolejną zmianą pobierz rekord po UUID:

curl --fail-with-body --silent --show-error \
  --request GET \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}?fields=*"

Odpowiedź zawiera data.attributes oraz data.meta.etag. Serwer zwraca tę samą wartość także w nagłówku HTTP:

HTTP/1.1 200 OK
ETag: "{ETAG_AFTER_GET}"

Po każdej udanej mutacji ETag może się zmienić, również po zmianie relacji, pinowaniu, decyzji i operacji na plikach. Zawsze zapisuj wartość zwróconą przez ostatnią udaną operację.


Akceptacje - edycja pól z If-Match

Zwykła edycja zmienia tylko pola biznesowe. Nie zapisuj przez nią statusu, dat decyzji ani pinu:

curl --fail-with-body --silent --show-error \
  --request PATCH \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{ETAG_AFTER_GET}"' \
  --header "Idempotency-Key: public-api-approval-update-20260906060644" \
  --data '{
    "attributes": {
      "info": "Updated approval information from the integration.",
      "level": "Manager",
      "category": "Approved procurement",
      "description": "Approval edited through the Codenica Public API."
    }
  }' \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}"

Sukces zwraca 200 OK i nowy ETag. Zmień tylko te pola, które rzeczywiście uległy zmianie. Ułatwia to rozwiązywanie konfliktów i ogranicza ryzyko nadpisania danych.


Akceptacje - wymagany If-Match i ochrona przed konfliktem

Aktualny ETag jest warunkiem zmiany istniejącego rekordu. PATCH bez tego nagłówka kończy się odpowiedzią:

{
  "type": "https://docs.codenica.com/errors/if_match_required",
  "title": "Precondition required.",
  "status": 428,
  "code": "if_match_required",
  "detail": "Send the ETag returned by GET in the If-Match header."
}

Jeżeli przesłany ETag jest nieaktualny, API zwraca 412 Precondition Failed i kod if_match_failed:

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

Żądanie odrzucone przez 412 nie zapisuje zmian. Pobierz Akceptację ponownie, porównaj dane i dopiero wtedy przygotuj świadomą aktualizację. Nie nadpisuj automatycznie zmian wykonanych przez drugą osobę lub integrację.


Akceptacje - pinowanie

Pin jest polem odczytowym zmienianym przez osobny endpoint. Dozwolone wartości to liczby całkowite od 0 do 3:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-pin-20260906060644" \
  --data '{"pin":3}' \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/pin"

Aby usunąć pin, wyślij null przez ten sam endpoint:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{ETAG_AFTER_PIN}"' \
  --header "Idempotency-Key: public-api-approval-unpin-20260906060644" \
  --data '{"pin":null}' \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/pin"

Obie operacje wymagają aktualnego ETag oraz approvals:pin:write. Nie wysyłaj pin w zwykłym PATCH.


Akceptacje - wykonanie decyzji Approved albo Rejected

Decyzja ma dedykowany endpoint:

/api/v1/approvals/{APPROVAL_ID}/decision

Pozytywna decyzja ustawia status=Approved, dateApproved i dateEnd:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-decision-approved-20260906060644" \
  --data '{"approved":true,"remark":"Approved through the Public API integration."}' \
  "$BASE_URL/api/v1/approvals/{APPROVED_APPROVAL_ID}/decision"

Negatywna decyzja ustawia status=Rejected, dateRejected i dateEnd:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-decision-rejected-20260906060644" \
  --data '{"approved":false,"remark":"Rejected through the Public API integration."}' \
  "$BASE_URL/api/v1/approvals/{REJECTED_APPROVAL_ID}/decision"

Nie zmieniaj statusu przez PATCH, aby ominąć tę procedurę. Decyzja wymaga approvals:decision:write, właściwego uprawnienia biznesowego (Approval_Accept albo Approval_Reject), aktualnego ETag oraz wywołania przez dokładnie tego użytkownika, który został przypisany jako approver.


Akceptacje - requester i approver

Relacje z użytkownikami odczytuje się przez osobny endpoint:

curl --fail-with-body --silent --show-error \
  --request GET \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/user-relationships?page=1&pageSize=20"

W odpowiedzi znajdziesz relację requester oraz, jeżeli wskazano approvera, relację approver:

{
  "data": {
    "items": [
      {
        "userId": "{USER_ID_REQUESTERA}",
        "relationshipType": "requester"
      },
      {
        "userId": "{USER_ID_APPROVERA}",
        "relationshipType": "approver"
      }
    ]
  }
}

Requester jest ustawiany automatycznie na użytkownika tworzącego Akceptację. Obie relacje są odczytowe. Nie próbuj zmieniać approvera przez POST na user-relationships; przypisanie approvera należy do kontrolowanego procesu tworzenia lub operacji systemowej.


Akceptacje - dozwolone relacje z obiektami

Akceptacja ma ograniczony katalog celów relacji:

targetDataSet
targetItemType
notes
note
worktasks
worktask
requesteditems
requesteditem
tickets
ticket
changes
change
problems
problem
releases
release

W tym katalogu nie ma relacji z assets, clients, vendors, documents, confirmations ani z samą Akceptacją.

{
  "targetId": "{TARGET_ID}",
  "targetDataSet": "notes",
  "targetItemType": "note"
}

Najpierw wybierz rekord celu z listy odpowiedniego zbioru. Nie zakładaj, że każda baza zawiera rekord w każdym z siedmiu zbiorów.


Akceptacje - ważna różnica w formacie relacji

W relacjach obiektowych Akceptacji nie przechowuje się relationshipType. Body zawiera wyłącznie identyfikator celu, nazwę zbioru i techniczny typ obiektu:

{
  "targetId": "7bdda87a-6c37-49ae-9e40-272a7a9b8616",
  "targetDataSet": "notes",
  "targetItemType": "note"
}

Nie wysyłaj takiego pola:

{
  "targetId": "{TARGET_ID}",
  "targetDataSet": "notes",
  "targetItemType": "note",
  "relationshipType": "related"
}

relationshipType jest używane przez inne modele relacji oraz przez pliki, ale dla relacji Akceptacji z obiektami procesowymi zostanie odrzucone.

curl --fail-with-body --silent --show-error \
  --request GET \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/notes?page=1&pageSize=10"

Do wyboru celu potrzebujesz zakresu read danego zbioru, na przykład notes:read.


Akceptacje - dodanie, odczyt i usunięcie relacji

Do bezpośredniego dodawania relacji potrzebujesz aktualnego ETag Akceptacji oraz approvals:relationships:write:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-relation-add-0001" \
  --data '{"targetId":"{NOTE_ID}","targetDataSet":"notes","targetItemType":"note"}' \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/relationships"

Odczytaj relacje po dodaniu:

curl --fail-with-body --silent --show-error \
  --request GET \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/relationships?targetDataSet=notes&page=1&pageSize=100"

Usunięcie pojedynczej relacji wymaga nowego ETag otrzymanego po dodaniu:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{ETAG_AFTER_RELATION_ADD}"' \
  --header "Idempotency-Key: public-api-approval-relation-delete-0001" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/relationships/notes/{NOTE_ID}"

Po każdym zapisie ponownie pobierz kolekcję i potwierdź, że cel został dodany albo usunięty.


Akceptacje - relacje batch i relacje w PATCH

Wiele relacji możesz zmienić jednym żądaniem:

{
  "add": [
    {
      "targetId": "{NOTE_ID}",
      "targetDataSet": "notes",
      "targetItemType": "note"
    }
  ],
  "remove": []
}
curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-relationship-batch-0001" \
  --data-binary @relationship-batch.json \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/relationships:batch"

Odpowiedź zawiera liczniki added, removed i skipped. Możesz też użyć w PATCH pól relationshipsToAdd i relationshipsToRemove:

{
  "attributes": {
    "info": "Updated together with a relationship."
  },
  "relationshipsToAdd": [
    {
      "targetId": "{TICKET_ID}",
      "targetDataSet": "tickets",
      "targetItemType": "ticket"
    }
  ],
  "relationshipsToRemove": []
}

W obu wariantach obowiązują aktualny ETag, idempotencja i zakres relacji.


Akceptacje - lista plików i upload

Pliki są odrębnymi zasobami powiązanymi z Akceptacją. Najpierw odczytaj bieżącą listę:

curl --fail-with-body --silent --show-error \
  --request GET \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/files?page=1&pageSize=100"

Wyślij plik jako multipart/form-data. Rola pliku jest przekazywana w query stringu:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-file-upload-0001" \
  --form "[email protected];type=application/pdf" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/files?relationshipType=decision-form"

Nazwa pliku powinna być pojedynczą nazwą bez ścieżki. Przed wysłaniem sprawdź rozmiar i świadomie ustaw typ MIME. Upload wymaga approvals:files:write.

{
  "data": {
    "id": "{FILE_ID}",
    "fileName": "formularz-akceptacji.pdf",
    "contentType": "application/pdf",
    "size": 48231,
    "relationshipType": "decision-form",
    "isMain": false,
    "downloadUrl": "/api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}/content"
  }
}

Dla Akceptacji API nie udostępnia operacji ustawienia pliku głównego. Zwracane pliki mają isMain=false. Nie buduj integracji, która oczekuje endpointu /main dla tego obiektu.


Akceptacje - pobieranie, podpinanie i usuwanie plików

Treść pliku pobierz przez endpoint content i zapisz w trybie binarnym:

curl --fail-with-body --silent --show-error \
  --output pobrany-formularz-akceptacji.pdf \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}/content

Jeżeli plik istnieje już w systemie i masz jego File ID, możesz podpiąć go do Akceptacji bez ponownego uploadu:

curl --fail-with-body --silent --show-error \
  --request POST \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{ETAG_AKCEPTACJI}"' \
  --header "Idempotency-Key: public-api-approval-file-attach-0001" \
  "$BASE_URL/api/v1/approvals/{TARGET_APPROVAL_ID}/files/{FILE_ID}?relationshipType=reference"

Usunięcie pliku z Akceptacji:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-file-delete-0001" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}"

Attach tworzy powiązanie z istniejącym plikiem, ale nie przesyła nowej kopii. Upload, attach i delete zmieniają ETag Akceptacji. Download jest operacją odczytową.


Akceptacje - operacje batch

Endpoint /api/v1/approvals:batch pozwala tworzyć, edytować i usuwać wiele rekordów. Nie zastępuje operacji decyzji, pinowania, relacji ani plików:

{
  "items": [
    {
      "operation": "create",
      "create": {
        "itemType": "approval",
        "approverId": "{USER_ID_APPROVERA}",
        "attributes": {
          "customId": "PUBLIC-API-APPROVAL-BATCH-A",
          "location": "Warsaw",
          "department": "IT",
          "level": "Supervisor",
          "category": "Procurement",
          "description": "Batch-created Approval A"
        }
      }
    },
    {
      "operation": "create",
      "create": {
        "itemType": "approval",
        "approverId": "{USER_ID_APPROVERA}",
        "attributes": {
          "customId": "PUBLIC-API-APPROVAL-BATCH-B",
          "location": "Warsaw",
          "department": "IT",
          "level": "Manager",
          "category": "Procurement",
          "description": "Batch-created Approval B"
        }
      }
    }
  ]
}
curl --fail-with-body --silent --show-error \
  --request POST \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: public-api-approval-batch-create-0001" \
  --data-binary @approvals-batch-create.json \
  "$BASE_URL/api/v1/approvals:batch"

W jednym batchu możesz mieszać operacje create, update i delete. Każda aktualizacja i usunięcie musi mieć własny id oraz aktualny ifMatch.

{
  "data": {
    "items": [
      {
        "index": 0,
        "operation": "create",
        "status": 201,
        "id": "{BATCH_ID_A}",
        "data": {
          "id": "{BATCH_ID_A}",
          "meta": { "etag": "{ETAG_A}" }
        }
      }
    ],
    "succeeded": 2,
    "failed": 0
  }
}

Batch nie jest transakcją all-or-nothing. Przy częściowym wyniku możesz otrzymać 207 Multi-Status. Analizuj każdy element odpowiedzi i nie ponawiaj operacji, które już zakończyły się sukcesem.


Akceptacje - usunięcie rekordu

Przed usunięciem pobierz rekord ponownie, sprawdź UUID i aktualny ETag oraz upewnij się, że proces biznesowy pozwala na usunięcie:

curl --fail-with-body --silent --show-error \
  --request DELETE \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header 'If-Match: "{AKTUALNY_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-delete-0001" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}"

Poprawna odpowiedź zwraca 200 OK i data=true. Po usunięciu ponowny odczyt UUID powinien zwrócić 404 Not Found z kodem approval_not_found. Kontrolę możesz wykonać także przez listę filtrowaną po customId i oczekiwać totalItems=0.

Nie usuwaj Akceptacji bez potwierdzenia ETag. Chroni to integrację przed skasowaniem nowszej wersji rekordu przez proces pracujący na starej kopii.


Akceptacje - błędy, limity i bezpieczna kolejność pracy

Błędy mają format Problem Details. W logach zapisuj status, code i requestId, ale nie zapisuj Client Secret ani pełnych nagłówków:

HTTP
Kod
Reakcja
401
authentication_failed
Sprawdź adres i oba nagłówki.
403
approval_approver_required
Decyzję musi wykonać przypisany approver.
404
approval_not_found
Rekord nie istnieje albo nie jest widoczny.
409
approval_unique_constraint lub approval_concurrency_conflict
Usuń duplikat albo odczytaj rekord i jego nowy ETag.
412 / 428
if_match_failed, if_match_required
Pobierz ETag ponownie i dodaj wymagany nagłówek.
422
validation_failed, approval_decision_rejected, approval_pin_rejected
Popraw body albo sprawdź reguły procesu.
429
rate_limit_exceeded
Zastosuj opóźnienie rosnące i odczytaj Retry-After.

Odczytuj X-RateLimit-Limit i X-RateLimit-Remaining. Cache’uj schema i wartości, ogranicz równoległość, a przy 429 stosuj backoff.

Bezpieczna kolejność pracy to: context, schema, wybór approvera, lista lub odczyt, utworzenie z Idempotency-Key, zapisanie UUID i ETagu, relacje lub pliki, edycja z If-Match, decyzja przez /decision, odczyt weryfikacyjny i dopiero na końcu usunięcie. Ten sam układ można wykorzystać w n8n, przekazując UUID, ETag i klucze idempotencji między kolejnymi krokami.