Zapotrzebowania w Codenica API

Pracę z Zapotrzebowaniami 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 zbioru w API to requesteditems, a typ pojedynczego obiektu to requesteditem. Zapotrzebowanie służy do zapisania potrzeby zakupu, dostarczenia, przygotowania lub wykonania określonej pozycji. Oprócz opisu może zawierać termin, ilość, cenę, koszt, wartość, podatek, budżet, priorytet i status.

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


Zapotrzebowania - adres API i wybór instalacji

Wszystkie trasy dotyczące Zapotrzebowań zaczynają się od:

{BASE_URL}/api/v1/requesteditems

BASE_URL oznacza adres serwera Codenica bez końcówki /api/v1. W wersji Cloud użyj publicznej domeny przypisanej do właściwej firmy:

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 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://api.twoja-firma.example"

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


Zapotrzebowania - zakresy klucza API

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

requesteditems:read
requesteditems:write
requesteditems:delete
requesteditems:schema
requesteditems:stats
requesteditems:relationships:read
requesteditems:relationships:write
requesteditems:users:read
requesteditems:files:read
requesteditems:files:write
requesteditems:technical:read
requesteditems:technical:write
requesteditems:pin:write

Do samego odczytu list i rekordów wybierz requesteditems:read, a do sprawdzania katalogu pól także requesteditems:schema. Tworzenie i edycja wymagają requesteditems:write, usuwanie wymaga requesteditems:delete. Zakresy relacji, plików, statystyk, requestera i pinowania dodaj dopiero wtedy, gdy integracja będzie korzystać z tych operacji.

Jeśli integracja wyszukuje cele relacji, klucz musi mieć także odpowiednie zakresy odczytu, na przykład assets:read, clients:read, vendors:read, documents:read, tickets:read, changes:read, problems:read, releases:read, notes:read, approvals:read lub worktasks:read. Zakres klucza nie zastępuje uprawnień użytkownika.


Zapotrzebowania - 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/requesteditems?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 Zapotrzebowania i nie jest sekretem.


Zapotrzebowania - 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ść requesteditems w data.capabilities.resources, zakresy klucza i limity żądań.

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.


Zapotrzebowania - 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/requesteditems/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ść requesteditem. Dla każdego pola sprawdź readable, writable, required, technical, unique i maxLength.

Przykładowy fragment schematu:

{
  "data": {
    "itemType": "requesteditem",
    "fields": [
      { "name": "title", "type": "string", "writable": true },
      { "name": "dateDue", "type": "dateTime", "writable": true },
      { "name": "quantity", "type": "integer", "writable": true },
      { "name": "value", "type": "number", "writable": true },
      { "name": "pin", "type": "integer", "writable": false }
    ],
    "relationshipTargets": [
      { "targetDataSet": "assets", "targetItemType": "asset" },
      { "targetDataSet": "documents", "targetItemType": "document" },
      { "targetDataSet": "worktasks", "targetItemType": "worktask" }
    ]
  }
}

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.


Zapotrzebowania - pola biznesowe i systemowe

Najważniejsze pola Zapotrzebowania to:

Pole
Typ
Limit lub zastosowanie
customId
string
500 znaków, identyfikator po stronie integracji
dateDue, dateEnd
dateTime
termin realizacji i data końcowa
location, department
string
300 znaków każde
tag, link
string
2000 znaków każde
title
string
1000 znaków
status, priority, category, budget, currency
string
wartości opisujące proces i rozliczenie
tax, quantity
integer
liczba całkowita
cost, price, value
number
koszt, cena jednostkowa i wartość
description
string
10000 znaków

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.

appUserRequesterId
clientRequesterId
catalogId
catalogItemId
creator
updater
dateCreated
dateUpdated
importId
importSource
dateImported

Zapotrzebowania - dostępne endpointy

Najważniejsze trasy modułu requesteditems to:

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

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.


Zapotrzebowania - listowanie i paginacja

Listę Zapotrzebowań pobieraj stronami. Przykład:

curl --request GET --url "$BASE_URL/api/v1/requesteditems?itemType=requesteditem&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": "requested-item-uuid",
        "itemType": "requesteditem",
        "attributes": {
          "customId": "ERP-REQ-2026-0042",
          "title": "Trzy monitory do nowego stanowiska",
          "status": "Open",
          "quantity": 3,
          "value": 3136.5
        },
        "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. Nie zakładaj, że ostatnia strona zawsze zawiera mniej elementów niż wybrany pageSize. Maksymalny rozmiar strony sprawdź w data.capabilities.limits albo w aktualnym kontrakcie.


Zapotrzebowania - wyszukiwanie i filtry

Do wyszukiwania tekstowego użyj parametru search. Do synchronizacji lepiej stosować stabilny customId, UUID albo jawny filtr:

curl --silent --show-error -G \
  --data-urlencode "search=monitory" \
  --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/requesteditems"

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

curl --silent --show-error -G \
  --data-urlencode "status=Open" \
  --data-urlencode "priority=High" \
  --data-urlencode "customId=ERP-REQ-2026-0042" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/requesteditems"

Filtr strukturalny ma postać field:operator:value:

status:eq:Open
priority:ne:Low
quantity:gte:2
price:lt:1000
title:contains:laptop
customId:startswith:ERP-
link:notempty:

Obsługiwane operatory to 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.


Zapotrzebowania - 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,title,status,priority,dateDue,quantity,value" \
  --data-urlencode "include=files,relationships,users" \
  --data-urlencode "ids=7512ef99-0010-4962-9453-99383a377e4b" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/requesteditems"

Dostępne wartości include to files, relationships i users. Każda z nich wymaga odpowiedniego zakresu. fields=* nie omija uprawnień do pól technicznych i nie zwraca pól systemowych przeznaczonych do koperty odpowiedzi.

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.


Zapotrzebowania - 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=status" \
  --data-urlencode "limit=20" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  "$BASE_URL/api/v1/requesteditems/stats"

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

Przykładowa odpowiedź values:

{
  "data": {
    "field": "category",
    "values": ["Hardware", "Office"]
  },
  "meta": {
    "requestId": "request-id"
  }
}

Statystyki i wartości są odczytami i nie zmieniają Zapotrzebowań. Nie odpytywać ich w krótkiej pętli bez potrzeby - schema i wartości pól można przechowywać w pamięci podręcznej przez czas odpowiedni dla integracji.


Zapotrzebowania - minimalne utworzenie

Utworzenie rekordu wykonuje się przez POST /api/v1/requesteditems. W body podaj itemType oraz pola w attributes:

curl --request POST --url "$BASE_URL/api/v1/requesteditems" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: requesteditem-create-unique-001" \
  --data-raw '{
    "itemType": "requesteditem",
    "attributes": {
      "customId": "ERP-REQ-2026-0042",
      "title": "Zakup materiałów biurowych",
      "category": "Office",
      "quantity": 10,
      "currency": "PLN",
      "status": "Open",
      "description": "Pozycja utworzona przez integrację."
    }
  }'

Wartość itemType musi wynosić requesteditem. Nazwy i typy pól dopasuj do odpowiedzi schema. Daty wysyłaj jako ISO 8601, a liczby jako JSON numbers, bez formatowania ich jako tekst.

Poprawna odpowiedź ma status 201 Created. Zapisz data.id, ETag z nagłówka HTTP oraz ETag z data.meta.etag.


Zapotrzebowania - kompletne utworzenie z polami finansowymi

Poniższy przykład odpowiada rekordowi z pełnego przebiegu demonstracyjnego. Pokazuje termin, lokalizację, status, priorytet i dane rozliczeniowe:

curl --request POST --url "$BASE_URL/api/v1/requesteditems" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: requesteditem-create-20260906053922-source" \
  --data-raw '{
    "itemType": "requesteditem",
    "attributes": {
      "customId": "PUBLIC-API-REQUESTEDITEM-20260906053922-SOURCE",
      "dateDue": "2026-12-31T17:00:00Z",
      "dateEnd": "2027-01-15T17:00:00Z",
      "location": "Warsaw",
      "department": "IT",
      "tag": "public-api,requesteditems,demo",
      "link": "https://codenica.com",
      "title": "Requested Item API complete flow",
      "status": "Open",
      "priority": "High",
      "category": "Hardware",
      "budget": "IT-2026",
      "currency": "PLN",
      "tax": 23,
      "quantity": 3,
      "cost": 300,
      "price": 100,
      "value": 369,
      "description": "Demonstracyjne zapotrzebowanie utworzone przez Public API."
    },
    "customValues": [
      {
        "name": "description",
        "valuePattern": "[requested-item-demo] Public API"
      }
    ]
  }'

customValues jest opcjonalne. Usuń tę właściwość, jeśli integracja nie korzysta z reguł wartości dodatkowych. Nie wysyłaj pól technicznych tylko dlatego, że pojawiły się w odpowiedzi.


Zapotrzebowania - bezpieczne ponowienie utworzenia

Jeżeli po wysłaniu żądania wystąpi timeout i nie wiesz, czy rekord został zapisany, ponów dokładnie tę samą operację z tym samym Idempotency-Key i identycznym body:

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

Plik requesteditem-create.json musi zawierać dokładnie body z pierwszego żądania. Ponowienie zwróci ten sam rekord zamiast tworzyć duplikat. Nowa intencja biznesowa, zmienione body albo inna trasa wymagają nowego klucza. Ponowienie z innym body zwróci 422 idempotency_key_reused.

Klucz idempotencji przechowuj po stronie integracji razem ze statusem operacji. Nie używaj w tym celu Client Secret.


Zapotrzebowania - odczyt rekordu i ETag

Po utworzeniu albo znalezieniu Zapotrzebowania pobierz je po UUID:

export REQUESTED_ITEM_ID="7512ef99-0010-4962-9453-99383a377e4b"

curl --request GET --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID?include=files,relationships,users" \
  --header "Accept: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET"

Aktualny ETag znajdziesz w nagłówku HTTP oraz zwykle w data.meta.etag i głównej kopercie meta.etag:

ETag: "etag-value"

ETag jest nieprzezroczystą wersją konkretnego rekordu. Nie usuwaj cudzysłowów zwróconych w nagłówku i nie wyliczaj tej wartości samodzielnie. Przed każdą zmianą rekordu, relacji albo pliku pobierz świeży ETag, jeżeli rekord mógł zostać zmieniony przez inną osobę lub integrację.


Zapotrzebowania - częściowa edycja z If-Match

PATCH zmienia tylko pola przesłane w body. Wymaga aktualnego ETagu i nowego klucza idempotencji:

export REQUESTED_ITEM_ETAG='"etag-value-from-get"'

curl --request PATCH --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_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: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-update-20260906-0001" \
  --data-raw '{
    "attributes": {
      "dateDue": "2027-01-31T17:00:00Z",
      "title": "Requested Item API complete flow - updated",
      "status": "In progress",
      "priority": "Normal",
      "quantity": 4,
      "price": 125,
      "value": 615
    }
  }'

Nie musisz przesyłać całego obiektu. Pola pominięte w body pozostają bez zmian. Po odpowiedzi 200 OK zastąp zapisany ETag wartością zwróconą przez API. Każda kolejna mutacja musi użyć najnowszej wersji.


Zapotrzebowania - nieaktualny ETag i brak If-Match

Brak nagłówka If-Match jest odrzucany, aby integracja nie nadpisała zmian wykonanych przez inną osobę:

curl --request PATCH --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: requesteditem-update-without-etag-0001" \
  --data-raw '{"attributes":{"status":"Approved"}}'

Oczekiwany wynik to 428 Precondition Required z kodem if_match_required. Jeżeli wyślesz stary ETag, otrzymasz 412 Precondition Failed z kodem if_match_failed:

HTTP 412 Precondition Failed
code: if_match_failed

Po błędzie 412 pobierz rekord ponownie, porównaj własną zmianę z aktualnymi danymi i dopiero wtedy wykonaj kolejny PATCH. Nie uruchamiaj ślepej pętli, która nadpisuje zmiany użytkownika.


Zapotrzebowania - pinowanie i odpinanie

Pin jest polem tylko do odczytu w attributes. Ustawia się go osobnym endpointem, z aktualnym ETagiem:

curl --request POST --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/pin" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-pin-0001" \
  --data '{"pin":3}'

Dozwolone są wartości od 0 do 3. Aby usunąć przypięcie, użyj null:

curl --request POST --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/pin" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-unpin-0001" \
  --data '{"pin":null}'

Po każdej operacji zapisz nowy ETag. Nie próbuj zmieniać pin przez zwykły PATCH.


Zapotrzebowania - relacja requester

Każde Zapotrzebowanie może mieć systemową relację requester. Odczytaj ją przez:

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

Requester jest ustalany przez istniejący przepływ systemu i może wskazywać appUserRequesterId albo clientRequesterId. Public API udostępnia odczyt, ale nie ma osobnego POST ani DELETE do zmiany tej relacji. Nie próbuj ustawiać requestera przez nieudokumentowane pole w attributes. Odczyt wymaga requesteditems:users:read oraz odpowiednich uprawnień do danych.


Zapotrzebowania - dozwolone relacje z obiektami

Aktualny katalog celów relacji Zapotrzebowania obejmuje:

Zbiór
Przykładowy itemType
assets
asset
clients, vendors
client, vendor
documents, tickets
document, ticket
changes, problems, releases
change, problem, release
notes, approvals
note, approval
worktasks
worktask

Nie ma relacji z samym zbiorem requesteditems ani z confirmations. Cel musi być widoczny dla użytkownika przypisanego do klucza i zgodny z listą relationshipTargets zwróconą przez schema.


Zapotrzebowania - dodawanie, odczyt i usuwanie relacji

Relacja obiektowa nie przechowuje relationshipType. W payloadzie podaj targetId, targetDataSet i zgodny targetItemType:

{
  "targetId": "5ddc5b5a-5bdc-46f9-ab7e-5ad8f6775b3a",
  "targetDataSet": "assets",
  "targetItemType": "asset"
}

Dodanie relacji wymaga aktualnego ETagu źródła, zakresu requesteditems:relationships:write i osobnego klucza idempotencji:

curl --request POST --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/relationships" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-relationship-asset-0001" \
  --data '{
    "targetId": "5ddc5b5a-5bdc-46f9-ab7e-5ad8f6775b3a",
    "targetDataSet": "assets",
    "targetItemType": "asset"
  }'

Listę relacji odczytaj przez:

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

Pojedynczą relację usuwa się po zbiorze i UUID celu:

curl --request DELETE --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/relationships/assets/5ddc5b5a-5bdc-46f9-ab7e-5ad8f6775b3a" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-relationship-asset-delete-0001"

Po dodaniu albo usunięciu relacji odczytaj nowe dane źródłowe i zapisz nowy ETag. Jeżeli schema nie zwraca celu, nie używaj go w integracji.


Zapotrzebowania - grupowa zmiana relacji

Do kilku zmian w jednym żądaniu służy relationships:batch. W relacjach Zapotrzebowania nadal pomijaj relationshipType:

curl --request POST --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_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: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-relationship-batch-0001" \
  --data-raw '{
    "add": [
      {
        "targetId": "5ddc5b5a-5bdc-46f9-ab7e-5ad8f6775b3a",
        "targetDataSet": "assets",
        "targetItemType": "asset"
      },
      {
        "targetId": "88b93fb8-8848-4669-9d87-ed4795e13bcc",
        "targetDataSet": "documents",
        "targetItemType": "document"
      }
    ],
    "remove": [
      {
        "targetId": "8f42dc16-167b-4e4a-983f-862ae85f3c7a",
        "targetDataSet": "worktasks",
        "targetItemType": "worktask"
      }
    ]
  }'

Odpowiedź zawiera liczniki:

{
  "data": {
    "added": 2,
    "removed": 1,
    "skipped": 0
  },
  "meta": {
    "requestId": "request-id"
  }
}

skipped nie powinno być automatycznie traktowane jako sukces biznesowy. Po batchu odczytaj kolekcję relacji i sprawdź wynik każdej zmiany.


Zapotrzebowania - lista i upload plików

Pliki są obsługiwane osobno od pól Zapotrzebowania. Najpierw odczytaj bieżącą listę:

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

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

curl --request POST --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/files?relationshipType=request-form" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-file-upload-0001" \
  --form "[email protected];type=application/pdf"

Element pliku zawiera między innymi id, fileName, contentType, size, relationshipType, isMain i downloadUrl. Przed wysłaniem sprawdź rozmiar i świadomie ustaw typ MIME.


Zapotrzebowania - pobieranie, podpinanie i usuwanie plików

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

curl --request GET --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/files/$FILE_ID/content" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --output pobrane-zapotrzebowanie.pdf

Jeżeli plik istnieje już w systemie, możesz podpiąć go bez ponownego uploadu:

curl --request POST --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/files/$FILE_ID?relationshipType=quotation" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-file-attach-0001"

Usunięcie pliku z Zapotrzebowania:

curl --request DELETE --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID/files/$FILE_ID" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-file-delete-0001"

Attach tworzy relację z istniejącym plikiem, ale nie przesyła nowej kopii. Aktualny model Zapotrzebowań nie ma endpointu pliku głównego: każdy element ma isMain=false. Nie używaj /files/{FILE_ID}/main ani makeMain dla tego obiektu.


Zapotrzebowania - operacje batch

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

curl --request POST --url "$BASE_URL/api/v1/requesteditems:batch" \
  --header "Content-Type: application/json" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "Idempotency-Key: requesteditems-batch-20260906-0001" \
  --data-raw '{
    "items": [
      {
        "operation": "create",
        "create": {
          "itemType": "requesteditem",
          "attributes": {
            "customId": "ERP-REQ-BATCH-001",
            "title": "Mysz i klawiatura dla zespołu",
            "category": "Hardware",
            "quantity": 5,
            "price": 150,
            "currency": "PLN",
            "status": "Open"
          }
        }
      },
      {
        "operation": "update",
        "id": "7dc877ec-4766-42cc-a34a-900e55ab3f46",
        "ifMatch": "\"etag-from-get\"",
        "update": {
          "attributes": {
            "status": "Approved",
            "quantity": 6
          }
        }
      },
      {
        "operation": "delete",
        "id": "476b8c2e-6da9-409d-bb35-98039619ccfe",
        "ifMatch": "\"etag-after-update\""
      }
    ]
  }'

Każdy element update i delete ma własny ETag. Batch nie jest transakcją all-or-nothing. Przejdź po items w odpowiedzi, zapisz status, UUID i błąd dla każdego elementu. Przy częściowym wyniku możesz otrzymać 207 Multi-Status.


Zapotrzebowania - 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 --request DELETE --url "$BASE_URL/api/v1/requesteditems/$REQUESTED_ITEM_ID" \
  --header "X-Codenica-Client-Id: $CLIENT_ID" \
  --header "X-Codenica-Client-Secret: $CLIENT_SECRET" \
  --header "If-Match: $REQUESTED_ITEM_ETAG" \
  --header "Idempotency-Key: requesteditem-delete-20260906-0001"

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

Usunięcie rekordu nie jest sposobem na zachowanie historii procesu. Jeśli dane mają znaczenie audytowe, zapisz potrzebne informacje w systemie źródłowym przed wykonaniem DELETE.


Zapotrzebowania - 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ź host i oba nagłówki.
403
scope_or_access_denied
Sprawdź zakres i uprawnienia użytkownika.
404
requestedItem_not_found
Rekord nie istnieje albo nie jest widoczny.
412
if_match_failed
Pobierz nowy ETag i rozstrzygnij konflikt.
428
if_match_required lub idempotency_key_required
Dodaj wymagany nagłówek.
422
validation_failed
Popraw body zgodnie ze schema.
429
rate_limit_exceeded
Zastosuj opóźnienie rosnące i ewentualny Retry-After.

Odczytuj X-RateLimit-Limit i X-RateLimit-Remaining. Ogranicz równoległość, cache’uj schema i wartości, a przy 429 stosuj backoff. Bezpieczna kolejność integracji to: context, schema, lista lub odczyt po UUID, utworzenie z Idempotency-Key, zapisanie UUID i ETagu, pliki lub relacje, edycja z If-Match, odczyt weryfikacyjny i dopiero na końcu usunięcie. Ten sam układ można wykorzystać w n8n, jeśli poświadczenia są zapisane jako credential, a UUID, ETag i klucze idempotencji są przekazywane między węzłami.