Genehmigungen in der Codenica API

Um mit Genehmigungen über die Codenica API zu arbeiten, erstellen Sie zuerst einen API-Schlüssel in den Codenica-Einstellungen. Wenn Sie noch keinen Schlüssel erstellt haben, öffnen Sie Codenica API - Einführung in einem neuen Tab. Dort werden die gemeinsame Schlüsselerstellung, die sichere Aufbewahrung des Secrets und die Authentifizierungsregeln erklärt.

Der technische Objektname lautet approval, der Name der Sammlung in der API ist approvals. Eine Genehmigung enthält eine Anfrage, die für die Entscheidung zuständige Person, beschreibende Angaben und Verknüpfungen mit Prozessobjekten. Zusätzlich können Dateien und eine Anheftstufe vorhanden sein.

Der entscheidende Unterschied zu einer normalen Änderung besteht darin, dass das Ergebnis nicht direkt in status geschrieben wird. Eine Genehmigung wird über einen eigenen Decision-Endpunkt genehmigt oder abgelehnt. So kann die API prüfen, ob der richtige Genehmiger handelt und ob sich der Datensatz seit dem Lesen verändert hat.

Die Beispiele verwenden die Kennung PUBLIC-API-APPROVAL-20260906060644. Ersetzen Sie sie durch eine Kennung Ihrer Integrationsanwendung und passen Sie UUIDs und Feldwerte an Ihre Datenbank an.


Genehmigungen - API-Adresse und Installationsart

Alle Routen für Genehmigungen beginnen mit:

{BASE_URL}/api/v1/approvals

BASE_URL ist die Adresse des Codenica-Servers ohne das Suffix /api/v1. Verwenden Sie in der Cloud die tatsächliche, dem Unternehmen zugewiesene Domain:

export BASE_URL="https://{actual-company-domain}"

Bei einer standardmäßigen On-Premise-Installation lautet die von Codenica Discovery lokal registrierte Adresse:

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

Wenn ein Administrator die Installation über eine Unternehmensdomain, einen Reverse Proxy, HTTPS oder einen anderen Port bereitgestellt hat, verwenden Sie die dafür mitgeteilte exakte Adresse:

export BASE_URL="https://{actual-installation-address}"

Verwenden Sie localhost nicht, wenn die Integrationsanwendung auf einem anderen Computer als die API ausgeführt wird. Die Ziel-Datenbank wird anhand des Hosts der Anfrage ausgewählt. Senden Sie tenantId weder im Body noch in der Query-Zeichenfolge oder einem zusätzlichen Header.


Genehmigungen - Berechtigungsbereiche des API-Schlüssels

Der für Genehmigungen verwendete Schlüssel sollte nur die für die jeweilige Integration erforderlichen Berechtigungsbereiche enthalten. Der vollständige Bereich für dieses Modul ist:

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

Für Listen und das Lesen von Datensätzen benötigen Sie approvals:read. Für das Erstellen und Bearbeiten ist approvals:write erforderlich, für das Löschen approvals:delete. Ergänzen Sie Bereiche für Beziehungen, Dateien, Statistiken, technische Daten, Anheften und Entscheidungen nur dann, wenn die Integration diese Funktionen verwendet.

Wenn die Integration Beziehungsziele sucht, benötigt sie zusätzlich die passenden Lesebereiche der Sammlungen, aus denen Ziele ausgewählt werden, zum Beispiel notes:read, worktasks:read, requesteditems:read, tickets:read, changes:read, problems:read oder releases:read. Ein Bereich des Schlüssels ersetzt nicht die Benutzerberechtigungen.


Genehmigungen - Authentifizierung

Authentifizieren Sie jede Anfrage an die Codenica API mit zwei Headern:

export CLIENT_ID="cna_your_client_id"
export CLIENT_SECRET="cns_your_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"

Eine externe Integration benötigt weder ein Administrator-JWT noch Cookies aus dem Codenica-Panel. Speichern Sie das Secret in einem serverseitigen Secret-Speicher. Legen Sie es nicht in browserseitig ausgelieferten Code, ein Repository, eine URL, die Shell-History oder Protokolle. Verwenden Sie außerhalb lokaler Tests HTTPS.

Speichern Sie meta.requestId aus den Antworten. Damit lässt sich eine bestimmte Anfrage in den Protokollen finden. Es handelt sich jedoch weder um die UUID der Genehmigung noch um ein Secret.


Genehmigungen - Verbindungskontext prüfen

Rufen Sie vor dem ersten Schreibvorgang den Kontext ab. So bestätigen Sie, dass die Adresse die richtige Datenbank erreicht und der Schlüssel die erforderlichen Bereiche und Limits besitzt:

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"

Prüfen Sie data.apiVersion, data.contractVersion und data.tenant, den Wert api_key in data.caller.authentication, den Eintrag approvals in data.capabilities.resources, die Schlüsselbereiche und die Limits.

Wenn der Kontext auf ein anderes Unternehmen zeigt oder ein erforderlicher Bereich fehlt, korrigieren Sie die Adresse oder erstellen Sie einen Schlüssel mit den erforderlichen Berechtigungen. Versuchen Sie nicht, durch ein fremdes tenantId eine andere Datenbank zu erreichen.


Genehmigungen - Schema und Beziehungsziele

Das Schema ist die maßgebliche Quelle für die aktuellen Felder, ihre Typen, ihre Schreibbarkeit und die zulässigen Beziehungsziele:

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"

Die Antwort enthält data.itemType, data.fields und data.relationshipTargets. Für dieses Modul ist itemType gleich approval. Prüfen Sie für jedes Feld readable, writable, required, technical, unique, maxLength und hasAutoGeneration.

Beispiel für einen Schemaausschnitt:

{
  "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" }
    ]
  }
}

Bauen Sie das Mapping nicht allein anhand dieses Beispiels auf. Rufen Sie vor dem Start der Integration das Schema der tatsächlichen Datenbank ab und verwenden Sie nur die zurückgegebenen Felder und Ziele.


Genehmigungen - Geschäfts- und Systemfelder

Die wichtigsten Felder einer Genehmigung sind:

Feld
Typ
Zweck
customId
string
Kennung aus der Integrationsanwendung
location, department
string
Ort und Abteilung des Prozesses
tag, link
string
Kennzeichnungen und Link zur Quelle
info, description
string
Zusatzinformationen und Beschreibung der Anfrage
level, category
string
Genehmigungsstufe und Prozesskategorie
status, dateApproved, dateRejected
schreibgeschützt
Status und Zeitpunkte des Entscheidungsergebnisses
remark, pin
systemverwaltet
Entscheidungskommentar und Anheftstufe

id, itemType, creator, updater, dateCreated, dateUpdated, importId, importSource und dateImported werden vom System vergeben oder sind für technische Lesevorgänge vorgesehen. Senden Sie sie nicht in attributes.

creator
updater
dateCreated
dateUpdated
importId
importSource
dateImported

Genehmigungen - verfügbare Endpunkte

Die wichtigsten Routen des Moduls approvals sind:

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

Lesevorgänge benötigen die read-Bereiche; einzelne Änderungen benötigen zusätzlich die oben beschriebenen Bereiche. Jede Anfrage, die Daten verändert, benötigt außerdem Idempotency-Key. Vorgänge für einen bestehenden Datensatz benötigen zusätzlich das aktuelle If-Match.


Genehmigungen - Liste und Pagination

Rufen Sie Genehmigungslisten seitenweise ab:

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"

Die Antwort enthält data.items und die Seiteninformationen:

{
  "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"
  }
}

Wechseln Sie anhand von hasNextPage zur nächsten Seite. Lesen Sie die maximale Seitengröße aus data.capabilities.limits.maxPageSize, statt sie fest zu hinterlegen.


Genehmigungen - Suche und Filter

Verwenden Sie search für eine Textsuche. Für die Synchronisierung ist eine stabile customId oder UUID besser geeignet:

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

Einfache Filter können Feldnamen verwenden:

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"

Strukturelle Filter verwenden die Form field:operator:value:

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

Unterstützte Operatoren sind unter anderem eq, ne, gt, gte, lt, lte, contains, startswith, endswith und notempty. URL-kodieren Sie Filterwerte, besonders bei Leerzeichen, Doppelpunkten oder Sonderzeichen.


Genehmigungen - Feldauswahl und eingeschlossene Daten

Verwenden Sie fields, wenn Sie nur einen Teil der Antwort benötigen. Dateien, Beziehungen und Benutzer werden mit include eingeschlossen:

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"

Verfügbare include-Werte sind files, relationships und users. Jeder Wert kann einen eigenen Bereich erfordern. fields=* umgeht weder den Schutz technischer Felder noch die Zugriffsregeln.

Sie können außerdem mit createdAfter, createdBefore, updatedAfter, updatedBefore, sort und direction filtern. Prüfen Sie die Feldnamen im aktuellen Schema.


Genehmigungen - Statistiken und Feldwerte

Der Endpunkt stats zeigt die Verteilung der Daten, während values Werte für den Aufbau von Filtern zurückgibt:

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"

Beide Anfragen sind schreibgeschützt und ändern keine Genehmigungen. Das Feld muss laut Schema zulässig sein; die Werte hängen von den für den Benutzer sichtbaren Datensätzen ab.


Genehmigungen - minimale Erstellung

Ein sinnvoller Minimaldatensatz enthält den Typ, eine Kennung aus der Integrationsanwendung, eine Kategorie, eine Beschreibung und den Benutzer, der die Entscheidung treffen soll:

{
  "itemType": "approval",
  "attributes": {
    "customId": "ERP-APPROVAL-0001",
    "category": "Procurement",
    "description": "Genehmigung für den Kauf eines Monitors"
  },
  "approverId": "{APPROVER_USER_ID}"
}

Senden Sie den Datensatz als 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 bezeichnet den aktiven Codenica-Benutzer, der die Entscheidung trifft. Es handelt sich weder um die Kennung eines Kunden noch um eine beliebige E-Mail-Adresse.


Genehmigungen - vollständiges Beispiel für die Erstellung

Dieses Beispiel enthält Prozessinformationen, Kennzeichnungen, eine Genehmigungsstufe und eine technische Wertregel:

{
  "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": "Genehmigungsanfrage aus dem vollständigen Public-API-Ablauf.",
    "level": "Supervisor",
    "category": "Procurement",
    "description": "Über die Codenica Public API erstellte Genehmigung."
  },
  "approverId": "{APPROVER_USER_ID}",
  "customValues": [
    {
      "name": "description",
      "valuePattern": "[approval-example] PUBLIC-API-APPROVAL-20260906060644"
    }
  ]
}

approverId erfordert approvals:technical:write. customValues ist optional und erfordert ebenfalls den technischen Bereich. Verwenden Sie es nur für Felder, die das Schema zulässt.

Wählen Sie in einer echten Integration den Genehmiger entsprechend dem Unternehmensprozess. Der Benutzer, der den Datensatz erstellt, wird zum Antragsteller.


Genehmigungen - Erstellungsantwort und Idempotenzschlüssel

Eine erfolgreiche Erstellung liefert 201 Created, die Kennung des Datensatzes und den ersten ETag. Speichern Sie alle drei Informationen auf der Integrationsseite:

{
  "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}\""
  }
}

Jede schreibende Anfrage muss einen eigenen Idempotency-Key haben. Wenn der Client nicht weiß, ob die erste Anfrage den Server erreicht hat, senden Sie denselben Body noch einmal mit demselben Schlüssel:

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"

Durch die Wiederholung derselben Anfrage wird keine zweite Genehmigung erstellt. Ein anderer Body mit demselben Schlüssel wird abgelehnt, weil ein Schlüssel nur eine Operation darstellen darf.


Genehmigungen - einzelnen Datensatz und ETag lesen

Lesen Sie den Datensatz nach der Erstellung und vor jeder weiteren Änderung per 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=*"

Die Antwort enthält data.attributes und data.meta.etag. Der Server liefert denselben Wert auch in einem HTTP-Header:

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

Nach jeder erfolgreichen Änderung kann sich der ETag ändern, auch nach einer Beziehungsänderung, dem Anheften, einer Entscheidung oder einer Dateioperation. Speichern Sie immer den Wert der letzten erfolgreichen Operation.


Genehmigungen - Felder mit If-Match bearbeiten

Eine normale Aktualisierung ändert nur Geschäftsfelder. Verwenden Sie sie nicht, um Status, Entscheidungszeitpunkte oder die Anheftung zu schreiben:

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": "Aktualisierte Genehmigungsinformationen aus der Integration.",
      "level": "Manager",
      "category": "Approved procurement",
      "description": "Über die Codenica Public API bearbeitete Genehmigung."
    }
  }' \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}"

Bei Erfolg erhalten Sie 200 OK und einen neuen ETag. Aktualisieren Sie nur Felder, die sich tatsächlich geändert haben. Das erleichtert die Konfliktbehandlung und verringert das Risiko, Daten zu überschreiben.


Genehmigungen - erforderliches If-Match und Konfliktschutz

Zum Ändern eines bestehenden Datensatzes ist der aktuelle ETag erforderlich. Ein PATCH ohne diesen Header liefert:

{
  "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."
}

Wenn der übermittelte ETag veraltet ist, gibt die API 412 Precondition Failed und den Code if_match_failed zurück:

{
  "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."
}

Eine mit 412 abgelehnte Anfrage speichert keine Änderung. Lesen Sie die Genehmigung erneut, vergleichen Sie die Daten und erstellen Sie erst danach eine bewusste Aktualisierung. Überschreiben Sie Änderungen einer anderen Person oder Integration nicht automatisch.


Genehmigungen - Anheften

Das Feld pin ist schreibgeschützt und wird über einen eigenen Endpunkt geändert. Zulässig sind Ganzzahlen von 0 bis 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: "{CURRENT_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-pin-20260906060644" \
  --data '{"pin":3}' \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/pin"

Zum Entfernen der Anheftung senden Sie über denselben Endpunkt null:

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"

Beide Vorgänge erfordern den aktuellen ETag und approvals:pin:write. Senden Sie pin nicht in einem normalen PATCH.


Genehmigungen - eine Approved- oder Rejected-Entscheidung ausführen

Entscheidungen verwenden einen eigenen Endpunkt:

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

Eine positive Entscheidung setzt status=Approved, dateApproved und dateEnd:

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 'If-Match: "{CURRENT_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-decision-approved-20260906060644" \
  --data '{"approved":true,"remark":"Über die Public-API-Integration genehmigt."}' \
  "$BASE_URL/api/v1/approvals/{APPROVED_APPROVAL_ID}/decision"

Eine negative Entscheidung setzt status=Rejected, dateRejected und dateEnd:

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 'If-Match: "{CURRENT_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-decision-rejected-20260906060644" \
  --data '{"approved":false,"remark":"Über die Public-API-Integration abgelehnt."}' \
  "$BASE_URL/api/v1/approvals/{REJECTED_APPROVAL_ID}/decision"

Ändern Sie den Status nicht per PATCH, um den vorgesehenen Ablauf zu umgehen. Eine Entscheidung erfordert approvals:decision:write, die passende Geschäftsberechtigung (Approval_Accept oder Approval_Reject), einen aktuellen ETag und einen Aufruf genau durch den als Genehmiger zugewiesenen Benutzer.


Genehmigungen - Antragsteller und Genehmiger

Lesen Sie Benutzerbeziehungen über einen eigenen Endpunkt:

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"

Die Antwort enthält eine requester-Beziehung und, wenn ein Genehmiger angegeben wurde, eine approver-Beziehung:

{
  "data": {
    "items": [
      {
        "userId": "{REQUESTER_USER_ID}",
        "relationshipType": "requester"
      },
      {
        "userId": "{APPROVER_USER_ID}",
        "relationshipType": "approver"
      }
    ]
  }
}

Der Antragsteller wird automatisch dem Benutzer zugeordnet, der die Genehmigung erstellt. Beide Beziehungen sind schreibgeschützt. Versuchen Sie nicht, den Genehmiger per POST auf user-relationships zu ändern; seine Zuordnung gehört zur kontrollierten Erstellung oder zum Systemprozess.


Genehmigungen - zulässige Objektbeziehungen

Der Katalog der Beziehungsziele für Genehmigungen ist bewusst begrenzt:

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

Dieser Katalog enthält keine Beziehungen zu assets, clients, vendors, documents, confirmations oder zur Genehmigung selbst.

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

Wählen Sie zuerst einen Ziel-Datensatz aus der Liste der jeweiligen Sammlung. Gehen Sie nicht davon aus, dass jede Datenbank einen Datensatz in allen sieben Sammlungen enthält.


Genehmigungen - eine wichtige Besonderheit des Beziehungsformats

Objektbeziehungen von Genehmigungen speichern kein relationshipType. Der Body enthält nur die Zielkennung, den Namen der Sammlung und den technischen Objekttyp:

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

Senden Sie dieses Feld nicht:

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

relationshipType wird von anderen Beziehungsmodellen und von Dateien verwendet, ist bei Beziehungen von Genehmigungen zu Prozessobjekten jedoch nicht zulässig.

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"

Für die Auswahl eines Ziels benötigen Sie den read-Bereich der Sammlung, zum Beispiel notes:read.


Genehmigungen - Beziehung hinzufügen, lesen und entfernen

Das direkte Erstellen einer Beziehung erfordert den aktuellen ETag der Genehmigung und 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: "{CURRENT_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"

Lesen Sie die Beziehungen nach dem Hinzufügen:

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"

Entfernen Sie eine Beziehung mit dem neuen ETag, der nach dem Hinzufügen zurückgegeben wurde:

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}"

Rufen Sie nach jedem Schreibvorgang die Beziehungssammlung erneut ab und bestätigen Sie, dass das Ziel hinzugefügt oder entfernt wurde.


Genehmigungen - Beziehungsbatch und Beziehungen in PATCH

Ändern Sie mehrere Beziehungen mit einer Anfrage:

{
  "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: "{CURRENT_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-relationship-batch-0001" \
  --data-binary @relationship-batch.json \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/relationships:batch"

Die Antwort enthält die Zähler added, removed und skipped. Ein PATCH kann auch relationshipsToAdd und relationshipsToRemove enthalten:

{
  "attributes": {
    "info": "Zusammen mit einer Beziehung aktualisierte Informationen."
  },
  "relationshipsToAdd": [
    {
      "targetId": "{TICKET_ID}",
      "targetDataSet": "tickets",
      "targetItemType": "ticket"
    }
  ],
  "relationshipsToRemove": []
}

In beiden Varianten sind der aktuelle ETag, der Idempotenzschlüssel und der Beziehungsbereich erforderlich.


Genehmigungen - Dateiliste und Upload

Dateien sind eigene Ressourcen, die mit einer Genehmigung verknüpft werden. Lesen Sie zuerst die aktuelle Liste:

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"

Laden Sie eine Datei als multipart/form-data hoch. Die Rolle der Datei wird in der Query-Zeichenfolge übergeben:

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: "{CURRENT_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"

Der Dateiname muss ein einzelner Name ohne Pfad sein. Prüfen Sie die Größe vor dem Senden und setzen Sie den MIME-Typ bewusst. Für den Upload ist approvals:files:write erforderlich.

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

Die Genehmigungs-API bietet keine Operation zum Festlegen einer Hauptdatei. Zurückgegebene Dateien haben isMain=false. Bauen Sie keine Integration, die für dieses Objekt einen /main-Endpunkt erwartet.


Genehmigungen - Dateien herunterladen, anhängen und entfernen

Laden Sie den Dateiinhalt über den content-Endpunkt herunter und speichern Sie ihn als Binärdatei:

curl --fail-with-body --silent --show-error \
  --output downloaded-approval-form.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"

Wenn eine Datei bereits im System vorhanden ist und Sie ihre File ID kennen, hängen Sie sie an eine Genehmigung an, ohne eine weitere Kopie hochzuladen:

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: "{TARGET_APPROVAL_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-file-attach-0001" \
  "$BASE_URL/api/v1/approvals/{TARGET_APPROVAL_ID}/files/{FILE_ID}?relationshipType=reference"

Entfernen Sie eine Datei aus einer Genehmigung:

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: "{CURRENT_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-file-delete-0001" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}/files/{FILE_ID}"

Beim Anhängen wird eine Verbindung zu einer vorhandenen Datei hergestellt; es wird keine neue Kopie hochgeladen. Upload, Anhängen und Löschen ändern den ETag der Genehmigung. Das Herunterladen ist schreibgeschützt.


Genehmigungen - Batch-Operationen

Der Endpunkt /api/v1/approvals:batch erstellt, bearbeitet und löscht mehrere Datensätze. Er ersetzt keine Entscheidungen, Anheftungen, Beziehungen oder Dateioperationen:

{
  "items": [
    {
      "operation": "create",
      "create": {
        "itemType": "approval",
        "approverId": "{APPROVER_USER_ID}",
        "attributes": {
          "customId": "PUBLIC-API-APPROVAL-BATCH-A",
          "location": "Warsaw",
          "department": "IT",
          "level": "Supervisor",
          "category": "Procurement",
          "description": "Im Batch erstellte Genehmigung A"
        }
      }
    },
    {
      "operation": "create",
      "create": {
        "itemType": "approval",
        "approverId": "{APPROVER_USER_ID}",
        "attributes": {
          "customId": "PUBLIC-API-APPROVAL-BATCH-B",
          "location": "Warsaw",
          "department": "IT",
          "level": "Manager",
          "category": "Procurement",
          "description": "Im Batch erstellte Genehmigung 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"

Ein Batch kann create, update und delete kombinieren. Jede Aktualisierung und jede Löschung muss eine eigene id und den aktuellen ifMatch-Wert enthalten.

{
  "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
  }
}

Ein Batch ist keine Alles-oder-nichts-Transaktion. Ein Teilergebnis kann 207 Multi-Status zurückgeben. Analysieren Sie jedes Antwortobjekt und wiederholen Sie keine bereits erfolgreichen Vorgänge.


Genehmigungen - Datensatz löschen

Lesen Sie den Datensatz vor dem Löschen erneut, prüfen Sie UUID und aktuellen ETag und bestätigen Sie, dass der Geschäftsprozess das Löschen erlaubt:

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: "{CURRENT_ETAG}"' \
  --header "Idempotency-Key: public-api-approval-delete-0001" \
  "$BASE_URL/api/v1/approvals/{APPROVAL_ID}"

Bei Erfolg werden 200 OK und data=true zurückgegeben. Nach dem Löschen sollte das Lesen derselben UUID mit 404 Not Found und approval_not_found antworten. Sie können das Ergebnis auch über eine nach customId gefilterte Liste mit totalItems=0 prüfen.

Löschen Sie eine Genehmigung nicht ohne ETag-Prüfung. So verhindern Sie, dass eine neuere Version gelöscht wird, während Sie mit einer alten Kopie arbeiten.


Genehmigungen - Fehler, Limits und sichere Reihenfolge

Fehler verwenden das Format Problem Details. Protokollieren Sie status, code und requestId, aber niemals das Client Secret oder vollständige Header:

HTTP
Code
Empfohlene Reaktion
401
authentication_failed
Adresse und beide Header prüfen.
403
approval_approver_required
Die Entscheidung muss durch den zugewiesenen Genehmiger erfolgen.
404
approval_not_found
Der Datensatz ist nicht vorhanden oder nicht sichtbar.
409
approval_unique_constraint oder approval_concurrency_conflict
Duplikat entfernen oder Datensatz und neuen ETag lesen.
412 / 428
if_match_failed, if_match_required
Aktuellen ETag lesen und erforderlichen Header ergänzen.
422
validation_failed, approval_decision_rejected, approval_pin_rejected
Body korrigieren oder Prozessregeln prüfen.
429
rate_limit_exceeded
Zunehmendes Backoff anwenden und Retry-After lesen.

Lesen Sie X-RateLimit-Limit und X-RateLimit-Remaining. Cachen Sie Schema und Werte, begrenzen Sie die Parallelität und verwenden Sie nach 429 ein zunehmendes Backoff.

Eine sichere Reihenfolge ist: context, schema, Genehmiger auswählen, Datensatz auflisten oder lesen, mit Idempotency-Key erstellen, UUID und ETag speichern, Beziehungen oder Dateien hinzufügen, mit If-Match bearbeiten, die Entscheidung über /decision ausführen, erneut lesen und erst danach bei Bedarf löschen. Dieselbe Reihenfolge funktioniert in n8n, wenn UUIDs, ETags und Idempotenzschlüssel zwischen den Schritten weitergegeben werden.