API · v1 · stabil
CODEMETA OS Developer Center
Konsole öffnen
Foundation

Transitions

Statuswechsel laufen über eine generische, schema-übergreifende API. Ein Aufruf gegen /api/v1/<resource>/<id>/transitions/... wertet den Workflow der Entity aus, prüft Guards, fragt ggf. einen Dialog an, legt ggf. einen Approval-Request an, und schreibt am Ende den neuen Status atomar mitsamt Audit-Eintrag.

Hintergrund und Begriffe stehen unter Status-Maschine & Transitions.

Resources

MethodePfadZweck
GET/api/v1/<resource>/<id>/transitionsErlaubte Transitions ab aktuellem Status
POST/api/v1/<resource>/<id>/transitions/<transitionKey>Transition ausführen (empfohlen)
POST/api/v1/<resource>/<id>/transitionLegacy-Form mit toStatus im Body

<resource> ist die Plural-Collection (z. B. tickets), <transitionKey> ist der Zielstatus oder der Edge-Key des Custom-Workflows.

Verfügbare Transitions abfragen

curl "https://os.codemeta.de/api/v1/tickets/<id>/transitions" \
  -H "X-Tenant-Id: $TENANT" \
  -b cookies.txt

Antwort:

{
  "data": [
    {
      "key": "IN_PROGRESS",
      "label": "In Bearbeitung",
      "from": "OPEN",
      "to": "IN_PROGRESS",
      "guards": [{ "type": "requiredFields", "ok": true }],
      "available": true
    },
    {
      "key": "RESOLVED",
      "label": "Gelöst",
      "from": "OPEN",
      "to": "RESOLVED",
      "guards": [{ "type": "requireComment", "ok": false, "reason": "Kein Kommentar" }],
      "available": false
    }
  ]
}

Die Engine evaluiert Guards bereits gegen den aktuellen Entity-State (inkl. Linked-Checklists, Pending-Approvals, Comment-Count, Time-Entries, Sub-Tasks). UIs nutzen das, um nicht-verfügbare Buttons zu deaktivieren.

Status-Field-Override

Manche Entities (z. B. zukünftige Multi-Status-Workflows) tragen mehrere Statusfelder. ?statusField=<name> wählt das Feld, das bewertet werden soll. Default: status.

Transition ausführen

curl -X POST "https://os.codemeta.de/api/v1/tickets/<id>/transitions/RESOLVED" \
  -H "X-Tenant-Id: $TENANT" \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{}'

Optionaler Body

{
  "comment": "Behoben durch Patch X.Y",
  "fields": { "resolution": "fixed" },
  "dialogResponse": { "closeReason": "duplicate" }
}
FeldBedeutung
commentOptionaler Begleit-Kommentar (wird in die Audit-Historie gehängt)
fieldsZusätzliche Feld-Patches, die mit dem Statuswrite zusammen laufen
dialogResponseAntwort auf einen Dialog-Guard (siehe unten)

Erfolg

{
  "data": { "_id": "0192f2c0-…", "status": "RESOLVED", "version": 8,  },
  "transition": {
    "key": "RESOLVED",
    "fromState": "OPEN",
    "toState": "RESOLVED",
    "executedActions": []
  }
}

Die Antwort ist die volle Entity nach dem Statuswrite. SSE-Listener (/api/v1/<resource>/<id>/stream) bekommen ein entity-updated mit payload.action: 'transitioned' und from/to.

Dialog-Gates — 428 Precondition Required

Verlangt ein Guard ein Dialog (confirmDialog, inputDialog, formDialog, signatureDialog), antwortet der Server 428:

HTTP/1.1 428 Precondition Required
{
  "type": "dialog_required",
  "title": "Dialog Required",
  "status": 428,
  "detail": "This transition requires user interaction before proceeding.",
  "dialogConfig": {
    "type": "input",
    "title": "Closing reason",
    "inputLabel": "Reason",
    "inputRequired": true,
    "targetField": "closeReason"
  },
  "guardType": "inputDialog"
}

Der Client rendert den Dialog, erhebt die Antwort und ruft die Transition erneut auf — diesmal mit dialogResponse im Body. Der Server schreibt den Wert automatisch in das targetField; eine doppelte Übergabe in fields ist nicht nötig.

Approval-Gates — 202 Accepted

Ein requireApproval-Guard öffnet einen Approval-Request:

HTTP/1.1 202 Accepted
{
  "type": "approval_required",
  "title": "Approval Required",
  "status": 202,
  "detail": "Transition requires approval. A request has been sent.",
  "approvalRequestId": "0192f2c0-…",
  "approverType": "role",
  "approverValue": "manager"
}

Der Status wechselt noch nicht. Der Approver bestätigt über die Plattform-UI bzw. den Magic-Link — erst dann zieht die Transition durch (oder zieht bei Ablehnung in rejectionTargetStatus, sofern konfiguriert).

Guard-Failures — 422 Unprocessable Entity

Schlägt ein Guard fehl, der nicht durch Dialog/Approval auflösbar ist:

HTTP/1.1 422 Unprocessable Entity
{
  "type": "https://docs.codemeta.os/errors/transition-guard-failed",
  "title": "Transition Not Allowed",
  "status": 422,
  "detail": "The transition 'RESOLVED' cannot be executed in the current state.",
  "errorCode": "REQUIRE_COMMENT",
  "failedGuards": [
    { "type": "FIELD_REQUIRED", "message": "Mindestens ein Kommentar erforderlich" }
  ]
}

Mappen Sie Ihren Client auf errorCode (stabil), nicht auf detail.

Hook-Failures — 422 mit transition_hook_failed

Async-Hooks (sendEmail, sendNotification, webhook) laufen vor dem Statuswrite. Schlägt einer fehl, wird die Transition abgebrochen:

HTTP/1.1 422 Unprocessable Entity
{
  "type": "transition_hook_failed",
  "title": "Transition Hook Failed",
  "status": 422,
  "detail": "SMTP connection refused",
  "errorCode": "EMAIL_SEND_FAILED",
  "instance": "/tickets/<id>/transitions/RESOLVED"
}

Das Entity bleibt im alten Status — kein Halb-Schreib-Zustand.

Legacy-Form mit toStatus

curl -X POST "https://os.codemeta.de/api/v1/tickets/<id>/transition" \
  -H "X-Tenant-Id: $TENANT" \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"toStatus": "IN_PROGRESS"}'

Funktional identisch zur /transitions/<key>-Variante; bleibt aus Kompatibilitätsgründen erhalten. Bevorzugen Sie die Pfad-Variante.

Audit & SSE

  • Jede erfolgreiche Transition erzeugt einen Eintrag in der Audit-Historie mit fromto.
  • Detail-Streams (GET /api/v1/<resource>/<id>/stream) liefern entity-updated-Events mit payload.action: 'transitioned'.

Verwandt

Suche