Skip to content

ODP Approval Modes

ODP supports two distinct approval modes. Both use the same Workflow Engine under the hood but differ in what gets approved and how the main item is affected during review.


At a Glance

Content ModerationDirect Approval
Use caseReview content changes before publishingProcess approval, document sign-off, status transitions
Triggerversions.submititems.create, items.update, or manual
Main item during reviewUnchanged — changes live in a draft versionDirectly affected — item already exists/changed
On approveVersion delta applied to main item (promote)Workflow completes, optional action steps run
On rejectMain item untouched, version marked rejectedWorkflow follows reject path, item already has the data
Who creates the changeAuthor creates a version (draft)Author edits the item directly
Versioning requiredYesNo

Mode 1: Content Moderation (Version-Based)

When to use: Published content that must be reviewed before changes go live — articles, product pages, legal documents, landing pages.

Key principle: The published item never changes until a reviewer approves.

Flow

Author                          System                         Reviewer
  │                               │                               │
  ├── POST /versions ────────────>│ create draft (status: draft)  │
  ├── PATCH /versions/:id ───────>│ edit delta                    │
  ├── POST /versions/:id/submit ─>│ status → pending              │
  │                               ├── auto-start workflow ───────>│
  │                               │                               ├── GET /workflow-tasks
  │                               │                               ├── GET /versions/:id (review)
  │                               │                               │
  │                               │<── approve ───────────────────┤
  │                               ├── promote version             │
  │                               ├── main item updated           │
  │                               ├── version status → approved   │
  │                               │                               │
  │                               │<── reject ────────────────────┤
  │                               ├── main item UNCHANGED         │
  │                               ├── version status → rejected   │

Version Status Lifecycle

draft ──> pending ──> approved   (delta applied to main item)

              └──> rejected      (main item unchanged)

              └──> draft         (admin cancelled → re-editable)

Workflow Setup

json
{
  "name": "Article Review",
  "collection": "articles",
  "trigger_event": "versions.submit"
}

The workflow auto-starts when a version is submitted. On final approval, the engine promotes the version automatically.

API Endpoints (Author)

ActionMethodEndpoint
Create draftPOST/versions
Edit draftPATCH/versions/:id
Submit for reviewPOST/versions/:id/submit
Check statusGET/versions/:id
List my versionsGET/versions?filter[user_created][_eq]=$CURRENT_USER

Delta Format

The delta contains only changed fields, not the entire item:

json
{
  "collection": "articles",
  "item": "42",
  "key": "june-revision",
  "name": "Update headline and featured flag",
  "delta": {
    "title": "New Headline",
    "featured": true
  }
}

FE Preview

Merge delta onto the current item to render a full preview:

ts
const preview = { ...mainItem, ...version.delta };

Diff rendering: iterate Object.keys(delta), compare each value against mainItem[field].


Mode 2: Direct Approval (Item-Based)

When to use: Process-driven workflows where the item itself IS the subject of approval — purchase requests, leave forms, document sign-off, onboarding checklists, status change gates.

Key principle: The item already has the data. The workflow controls what happens next (status transitions, notifications, field updates).

Flow

Author                          System                         Reviewer
  │                               │                               │
  ├── POST /items/requests ──────>│ item created                  │
  │                               ├── auto-start workflow ───────>│
  │                               │   (trigger: items.create)     │
  │                               │                               ├── GET /workflow-tasks
  │                               │                               ├── GET /items/requests/:id
  │                               │                               │
  │                               │<── approve ───────────────────┤
  │                               ├── action: update_field        │
  │                               │   (status → approved)         │
  │                               ├── action: send_notification   │
  │                               │                               │
  │                               │<── reject ────────────────────┤
  │                               ├── action: update_field        │
  │                               │   (status → rejected)         │

Workflow Setup

json
{
  "name": "Purchase Request Approval",
  "collection": "purchase_requests",
  "trigger_event": "items.create",
  "trigger_filter": {
    "amount": { "_gt": 1000 }
  }
}
  • items.create — triggers when a new item is created
  • items.update — triggers on update (use trigger_filter to target specific changes)
  • manual — no auto-trigger, start via POST /workflow-instances

Trigger Filter

Only start the workflow when conditions are met:

json
{
  "trigger_filter": {
    "department": { "_eq": "finance" },
    "amount": { "_gte": 5000 }
  }
}

Action Steps

Automate side effects between approval steps:

Action TypeDescriptionExample
update_fieldUpdate a field on the itemSet status to approved
send_notificationSend notification to usersNotify the requester
promote_versionPromote a linked versionOnly used in content moderation

API Endpoints

ActionMethodEndpoint
Start manuallyPOST/workflow-instances
List instancesGET/workflow-instances?collection=purchase_requests
View instanceGET/workflow-instances/:id
CancelPOST/workflow-instances/:id/cancel

Shared: Reviewer Experience

Both modes share the same reviewer experience via the Task Inbox.

Task Inbox

GET /workflow-tasks         → my pending tasks
GET /workflow-tasks/count   → badge count
GET /workflow-tasks/all     → all tasks (admin)

Each task includes:

json
{
  "instance_id": "...",
  "step_id": "...",
  "workflow_name": "Article Review",
  "step_name": "Editor Review",
  "collection": "articles",
  "item_id": "42",
  "version_id": "ver-uuid",       // ← null for direct approval
  "status": "active",
  "activated_at": "2026-06-08T10:00:00Z",
  "timeout_at": "2026-06-09T10:00:00Z",
  "actions_available": ["approve", "reject", "delegate", "comment"]
}

version_id is the key differentiator:

  • Has value → content moderation task. Show version diff (delta vs main item).
  • Null → direct approval task. Show the item itself.

Reviewer Actions

ActionEndpointNotes
ApprovePOST /workflow-instances/:id/steps/:stepId/approveComment optional
RejectPOST /workflow-instances/:id/steps/:stepId/rejectComment required
DelegatePOST /workflow-instances/:id/steps/:stepId/delegateto_user required
CommentPOST /workflow-instances/:id/steps/:stepId/commentNo state change

Approval Modes

Each approval step can be configured with:

ModeDescription
anyOne assignee approves → step passes
allEvery assignee must approve
majority>50% must approve
countExactly N approvals needed

Timeouts

Steps can auto-resolve after a deadline:

Timeout ActionEffect
auto_approveStep approved automatically
auto_rejectStep rejected automatically
escalateTask reassigned to escalate_to user
notifyNotification sent, step stays active

Permission Model

Two independent permission modules control access:

Versioning Permissions (versioning.*)

ActionDescriptionScope
versioning.viewView versions, compare with main itemPer collection
versioning.createCreate/edit drafts, submit for reviewPer collection
versioning.promoteManual promote (outside workflow)Per collection
versioning.manageDelete versions, admin opsPer collection

Ownership rule: only the version creator (or admin) can submit a version.

Workflow Permissions (workflow.*)

ActionDescriptionScope
workflow.viewView workflow definitions and instancesGlobal
workflow.startStart workflow instancesPer collection
workflow.participateApprove, reject, delegate, commentPer collection
workflow.manageCreate/edit workflows, cancel instances, view all tasksGlobal

Pre-built Policies

PolicyPermissionsTypical Role
Version Authorversioning.view + versioning.createContent editors
Version Reviewer+ versioning.promoteSenior editors
Version Managerversioning.*Content admins
Workflow Participantworkflow.view + workflow.start + workflow.participateAll staff
Workflow Managerworkflow.*System admins
News Reviewerworkflow.participate scoped to newsNews desk

Policies are assigned to roles or users via odp_accessodp_policiesodp_app_permissions.

Disable all app permission checks: ENABLE_APP_PERMISSIONS=false.


FE Implementation Guide

Deciding Which Mode to Show

ts
// When rendering a task from the inbox:
if (task.version_id) {
  // Content moderation → show version diff UI
  const version = await api.get(`/versions/${task.version_id}`);
  const mainItem = await api.get(`/items/${task.collection}/${task.item_id}`);
  const changes = diffDelta(version.delta, mainItem);
  // Render: field-by-field comparison table
} else {
  // Direct approval → show the item itself
  const item = await api.get(`/items/${task.collection}/${task.item_id}`);
  // Render: item detail view with approve/reject buttons
}

Content Moderation UI Components

ComponentData SourcePurpose
Edit Version FormGET /items/:collection/:id + form fieldsCreate draft with changed fields
Version ListGET /versions?filter[item][_eq]=:idShow all versions for an item
Diff Viewversion.delta vs main itemField-by-field comparison
Preview{ ...mainItem, ...version.delta }Full preview after promotion
Status Badgeversion.statusdraft / pending / approved / rejected
Submit ButtonPOST /versions/:id/submitOnly visible when status === 'draft'

Direct Approval UI Components

ComponentData SourcePurpose
Item DetailGET /items/:collection/:idShow the item being reviewed
Instance TimelineGET /workflow-instances/:idStep history + actions
Action Buttonstask.actions_availableShow only available actions

Shared UI Components

ComponentData SourcePurpose
Task InboxGET /workflow-tasksList of pending tasks
Inbox BadgeGET /workflow-tasks/countNotification count
Approve/RejectPOST .../approve or .../rejectDecision buttons
Comment ThreadGET /workflow-instances/:idactionsAudit trail
Delegate ModalPOST .../delegateReassign task

Polling Strategy

WhatEndpointIntervalWho
Inbox badgeGET /workflow-tasks/count30sAll reviewers
Version statusGET /versions/:id10s (while pending)Author watching their submission
Instance stateGET /workflow-instances/:id10s (while running)Anyone viewing the instance

Multi-Step Examples

Content: Article → Editor → Legal → Publish

Start ──> Editor Review ──approve──> Legal Review ──approve──> End (auto-promote)
               │                          │
               └──reject──> End           └──reject──> End

Process: Leave Request → Manager → HR

Start ──> Manager Approval ──approve──> HR Confirmation ──approve──> [update_field: status=approved] ──> End
                │                              │
                └──reject──> [update_field: status=rejected] ──> End

Conditional: Purchase Order by Amount

Start ──> [condition: amount >= 10000?]
              │yes                │no
              v                   v
        Director Approval    Manager Approval
              │                   │
              └───── approve ─────┘

                       v
                [send_notification] ──> End

API Reference

Versioning

POST /versions — Create a draft version

Create a new version containing proposed changes to an item. The version starts in draft status.

Permission: versioning.create (scoped to collection)

Request Body:

json
{
  "collection": "articles",
  "item": "42",
  "key": "june-revision",
  "name": "Update headline and featured flag",
  "delta": {
    "title": "New Headline",
    "featured": true
  }
}
FieldTypeRequiredDescription
collectionstringYesTarget collection name
itemstringYesPrimary key of the target item
keystringYesUnique key for this version (slug)
namestringNoHuman-readable name for display
deltaobjectNoObject containing only the fields being changed. Omitted fields remain unchanged on the main item.

Response 200:

json
{ "data": "version-uuid" }

GET /versions — List versions

List versions with optional filtering and pagination. Supports standard ODP query parameters.

Permission: versioning.view

Common Query Parameters:

ParamExampleDescription
filter[collection][_eq]articlesFilter by collection
filter[item][_eq]42Filter by item (show all versions of one item)
filter[user_created][_eq]$CURRENT_USERFilter by creator (my versions)
filter[status][_eq]pendingFilter by status
sort-date_createdSort order
limit25Max results
offset0Pagination offset
metatotal_count,filter_countInclude meta counts

Response 200:

json
{
  "data": [
    {
      "id": "version-uuid",
      "collection": "articles",
      "item": "42",
      "key": "june-revision",
      "name": "Update headline",
      "status": "draft",
      "delta": { "title": "New Headline" },
      "user_created": "user-uuid",
      "date_created": "2026-06-08T10:00:00Z",
      "date_updated": null
    }
  ],
  "meta": { "total_count": 12, "filter_count": 3 }
}

GET /versions/:id — Read a single version

Fetch one version with its full delta.

Permission: versioning.view (scoped to version's collection)

Response 200:

json
{
  "data": {
    "id": "version-uuid",
    "collection": "articles",
    "item": "42",
    "key": "june-revision",
    "name": "Update headline",
    "status": "pending",
    "delta": { "title": "New Headline", "featured": true },
    "hash": null,
    "user_created": "user-uuid",
    "user_updated": null,
    "date_created": "2026-06-08T10:00:00Z",
    "date_updated": "2026-06-08T10:05:00Z"
  }
}

PATCH /versions/:id — Update a draft version

Edit the delta or metadata of a version. Only meaningful while status === 'draft'.

Permission: versioning.create (scoped to version's collection)

Request Body (partial):

json
{
  "name": "Updated name",
  "delta": {
    "title": "Final Headline",
    "featured": true,
    "body": "Updated body text..."
  }
}

The delta is replaced entirely (not merged). Send the full delta object.

Response 200: { "data": "version-uuid" }


DELETE /versions/:id — Delete a version

Permanently remove a version record.

Permission: versioning.manage (scoped to version's collection)

Response 204: No content.


POST /versions/:id/submit — Submit for review

Transition a draft version to pending and trigger any matching versions.submit workflow on the collection. The workflow starts automatically — no manual POST /workflow-instances needed.

Permission: versioning.create (scoped to version's collection) + must be the version creator (or admin)

Validation rules:

RuleError
Version status must be draft400: Version cannot be submitted: current status is "..."
Version must have a non-empty delta400: Version has no changes to submit
No running workflow instance for this version400: A workflow is already running for this version

Response 200:

json
{ "data": { "id": "version-uuid", "status": "pending" } }

POST /versions/:id/promote — Manual promote

Apply the version's delta directly to the main item, bypassing workflow. Use this when no workflow is configured, or for admin overrides.

Permission: versioning.promote (scoped to version's collection)

After promotion:

  • Each field in delta is written to the main item
  • Version status changes to approved
  • Relational objects in delta are flattened to FK scalars (e.g. { user: { id: "..." } }user: "...")
  • Array/object values for JSON columns are serialized automatically

Response 204: No content.


Workflow Definitions

POST /workflows — Create a workflow definition

Permission: workflow.manage

Request Body:

json
{
  "name": "Article Review",
  "description": "Review articles before publishing",
  "collection": "articles",
  "trigger_event": "versions.submit",
  "trigger_filter": null,
  "options": null
}
FieldTypeRequiredDescription
namestringYesDisplay name
descriptionstringNoDescription
collectionstringNoBound collection. Required for auto-trigger workflows, null for manual-only.
trigger_eventstringYesversions.submit, items.create, items.update, or manual
trigger_filterobjectNoODP filter object. Only items matching this filter trigger the workflow.
optionsobjectNoAdditional configuration

New workflows start in draft status. They must be activated before they can trigger.

Response 201: { "data": { "id": "workflow-uuid" } }


GET /workflows — List workflow definitions

Permission: workflow.view

Query Parameters: status, collection, sort, limit, offset

Response 200: { "data": [ { ...workflow, steps: [...], transitions: [...] } ] }

Each workflow includes its full steps and transitions arrays.


GET /workflows/:id — Read a workflow definition

Permission: workflow.view

Returns the workflow with all steps and transitions.


PATCH /workflows/:id — Update a workflow

Permission: workflow.manage

Partial update. Can modify name, description, collection, trigger_event, trigger_filter, options.


DELETE /workflows/:id — Delete a workflow

Permission: workflow.manage

Cannot delete workflows that have running instances.

Response 204: No content.


POST /workflows/:id/duplicate — Duplicate a workflow

Permission: workflow.manage

Creates a new workflow in draft status with copies of all steps and transitions.

Response 201: { "data": { "id": "new-workflow-uuid" } }


POST /workflows/:id/activate — Activate a workflow

Permission: workflow.manage

Changes status from draftactive. Only active workflows respond to trigger events.

Response 200: { "data": { "id": "...", "status": "active" } }


POST /workflows/:id/deactivate — Deactivate a workflow

Permission: workflow.manage

Changes status from activedraft. Running instances are not affected.

Response 200: { "data": { "id": "...", "status": "draft" } }


Workflow Steps

Steps define the nodes of a workflow graph. Each workflow needs at least a start and end step.

POST /workflows/:id/steps — Add a step

Permission: workflow.manage

Request Body:

json
{
  "key": "editor_review",
  "name": "Editor Review",
  "type": "approval",
  "assign_type": "role",
  "assign_value": "editor-role-uuid",
  "approval_mode": "any",
  "approval_count": null,
  "timeout_minutes": 1440,
  "timeout_action": "auto_reject",
  "escalate_to": null,
  "action_type": null,
  "action_config": null,
  "position_x": 200,
  "position_y": 100,
  "sort_order": 2
}
FieldTypeWhenDescription
keystringAlwaysUnique identifier within the workflow
namestringAlwaysDisplay name
typestringAlwaysstart, approval, condition, action, notification, end
assign_typestringapprovalrole, user, field, creator, auto
assign_valuestringapprovalRole UUID, user UUID, field name, or null for creator/auto
approval_modestringapprovalany, all, majority, count
approval_countnumbercount modeRequired number of approvals
timeout_minutesnumberapprovalTimeout deadline in minutes (null = no timeout)
timeout_actionstringWhen timeout setauto_approve, auto_reject, escalate, notify
escalate_tostringescalate actionUser UUID to reassign to on timeout
action_typestringaction typeupdate_field, send_notification, promote_version
action_configobjectaction typeConfiguration for the action (field/value, recipients, etc.)
position_x/ynumberOptionalVisual canvas coordinates
sort_ordernumberOptionalDisplay order

Response 201: { "data": { "id": "step-uuid" } }


PATCH /workflows/:id/steps/:key — Update a step

Permission: workflow.manage

Partial update by step key.


DELETE /workflows/:id/steps/:key — Remove a step

Permission: workflow.manage

Response 204: No content.


Workflow Transitions

Transitions define the edges of the workflow graph — how flow moves between steps.

POST /workflows/:id/transitions — Add a transition

Permission: workflow.manage

Request Body:

json
{
  "from_step_id": "review-step-uuid",
  "to_step_id": "end-step-uuid",
  "trigger": "approve",
  "condition": null,
  "label": "Approved",
  "sort_order": 1
}
FieldTypeDescription
from_step_idUUIDSource step
to_step_idUUIDDestination step
triggerstringapprove, reject, timeout, condition, always
conditionobjectCondition expression (only for condition trigger)
labelstringDisplay label on the canvas edge
sort_ordernumberPriority when multiple transitions match

Trigger types:

TriggerWhen it fires
alwaysImmediately after the source step completes (used from start steps)
approveWhen the source step is approved
rejectWhen the source step is rejected
timeoutWhen the source step times out
conditionEvaluated against item data; fires if expression is truthy

Response 201: { "data": { "id": "transition-uuid" } }


PATCH /workflows/:id/transitions/:tid — Update a transition

Permission: workflow.manage


DELETE /workflows/:id/transitions/:tid — Remove a transition

Permission: workflow.manage

Response 204: No content.


Workflow Instances

An instance is a running execution of a workflow, bound to a specific item (and optionally a version).

POST /workflow-instances — Start an instance manually

Start a workflow for an item. Only needed for manual trigger workflows — items.create, items.update, and versions.submit workflows start automatically.

Permission: workflow.start (scoped to collection)

Request Body:

json
{
  "workflow_id": "workflow-uuid",
  "collection": "articles",
  "item_id": "42"
}

Response 201:

json
{
  "data": {
    "id": "instance-uuid",
    "workflow_id": "workflow-uuid",
    "collection": "articles",
    "item_id": "42",
    "version_id": null,
    "status": "running",
    "current_step_id": "first-step-uuid",
    "started_by": "user-uuid",
    "started_at": "2026-06-08T10:00:00Z",
    "completed_at": null
  }
}

GET /workflow-instances — List instances

Permission: workflow.view

Query Parameters:

ParamDescription
workflow_idFilter by workflow definition
collectionFilter by collection
item_idFilter by item
statusrunning, completed, rejected, cancelled, error
version_idFilter by version (content moderation)
limit / offsetPagination

Response 200: { "data": [ ...instances ] }


GET /workflow-instances/:id — Read an instance (full detail)

Returns the instance with enriched data: workflow step definitions, instance step states (with approval records), and the full action audit log.

Permission: workflow.view OR workflow.participate

Response 200:

json
{
  "data": {
    "id": "instance-uuid",
    "workflow_id": "workflow-uuid",
    "workflow_name": "Article Review",
    "collection": "articles",
    "item_id": "42",
    "version_id": "version-uuid",
    "status": "running",
    "current_step_id": "review-step-uuid",
    "started_by": "user-uuid",
    "started_at": "2026-06-08T10:00:00Z",
    "completed_at": null,
    "workflow_steps": [
      { "id": "...", "key": "start", "name": "Start", "type": "start" },
      { "id": "...", "key": "review", "name": "Editor Review", "type": "approval", "assign_type": "role", "assign_value": "..." }
    ],
    "instance_steps": [
      {
        "id": "instance-step-uuid",
        "step_id": "review-step-uuid",
        "status": "active",
        "approvals": [],
        "activated_at": "2026-06-08T10:00:05Z",
        "completed_at": null,
        "timeout_at": "2026-06-09T10:00:05Z"
      }
    ],
    "actions": [
      {
        "id": "action-uuid",
        "step_id": "start-step-uuid",
        "user_id": "user-uuid",
        "action": "start",
        "comment": null,
        "created_at": "2026-06-08T10:00:00Z"
      }
    ]
  }
}

Use this response to build:

  • Timeline: iterate actions sorted by created_at
  • Step progress: map instance_steps to workflow_steps by step_id
  • Current assignees: check workflow_steps where step_id === current_step_id

POST /workflow-instances/:id/cancel — Cancel a running instance

Stop a workflow in progress. If the instance has a linked version_id and the version is pending, the version reverts to draft (re-editable by the author).

Permission: workflow.manage

Request Body (optional):

json
{ "comment": "No longer needed" }

Response 200: { "data": { "id": "instance-uuid" } }


Step Actions (Reviewer)

These endpoints allow assigned reviewers to act on the current active step of an instance.

All step action endpoints require workflow.participate permission scoped to the instance's collection. Additionally, the engine enforces step assignment — only users assigned to the step (by role, direct assignment, or field) can act on it, unless the user is admin.

POST /workflow-instances/:id/steps/:stepId/approve — Approve

Approve the current step. If the step's approval_mode requirements are met (e.g. all assignees approved in all mode), the engine advances to the next step via the approve transition.

If this is the final step (transition leads to end) and the instance has a version_id, the version is automatically promoted (delta applied to main item).

Permission: workflow.participate (scoped to collection) + assigned to step

Request Body (optional):

json
{ "comment": "Looks good, approved." }

Response 200: { "data": { "success": true } }


POST /workflow-instances/:id/steps/:stepId/reject — Reject

Reject the current step. The engine follows the reject transition. If the instance has a version_id, the version status changes to rejected and the main item remains unchanged.

Permission: workflow.participate (scoped to collection) + assigned to step

Request Body:

json
{ "comment": "Title needs revision. Second paragraph is unclear." }

Comment is required for rejection.

Response 200: { "data": { "success": true } }


POST /workflow-instances/:id/steps/:stepId/delegate — Delegate

Reassign the task to another user. The original assignee is removed, and the target user becomes the new assignee. The target user must also have workflow.participate permission on the collection.

Permission: workflow.participate (scoped to collection) + assigned to step

Request Body:

json
{
  "to_user": "target-user-uuid",
  "comment": "Needs legal review, reassigning."
}
FieldTypeRequiredDescription
to_userUUIDYesUser to delegate to
commentstringNoReason for delegation

Response 200: { "data": { "success": true } }


POST /workflow-instances/:id/steps/:stepId/comment — Comment only

Add a comment to the step's audit log without changing any state. The step remains active, no transitions fire, no approvals are recorded. Use this for discussion, feedback, or questions before making a decision.

Permission: workflow.participate (scoped to collection)

Request Body:

json
{ "comment": "Consider rephrasing the introduction before I approve." }

Comment is required.

Response 200: { "data": { "success": true } }


Task Inbox

The task inbox surfaces pending approval tasks for the current user.

GET /workflow-tasks — My pending tasks

Returns all active tasks assigned to the current user (by role, direct assignment, or field-based resolution).

Permission: workflow.participate

Query Parameters: limit, offset

Response 200:

json
{
  "data": [
    {
      "id": "instance-step-uuid",
      "step_id": "step-uuid",
      "instance_id": "instance-uuid",
      "workflow_name": "Article Review",
      "step_name": "Editor Review",
      "collection": "articles",
      "item_id": "42",
      "version_id": "version-uuid",
      "status": "active",
      "activated_at": "2026-06-08T10:00:00Z",
      "timeout_at": "2026-06-09T10:00:00Z",
      "actions_available": ["approve", "reject", "delegate", "comment"]
    }
  ],
  "meta": { "total_count": 5 }
}

Key fields for FE:

FieldUsage
instance_id + step_idUsed in approve/reject/delegate/comment endpoint paths
version_idNon-null → content moderation task (show diff). Null → direct approval (show item).
actions_availableRender only these action buttons
timeout_atShow countdown timer. Null = no timeout.

GET /workflow-tasks/count — My task count

Returns a single number — the count of the current user's pending tasks. Use for inbox badge display.

Permission: workflow.participate

Response 200:

json
{ "data": { "count": 3 } }

GET /workflow-tasks/all — All active tasks (admin)

Returns all active tasks across all users, regardless of assignment.

Permission: workflow.manage

Query Parameters: limit, offset

Same response shape as GET /workflow-tasks.


Audit Log

GET /workflow-instances/:id/actions — Instance action history

Full audit trail for a specific workflow instance. Every approve, reject, delegate, comment, start, cancel, timeout, and escalation is recorded.

Permission: workflow.view OR workflow.participate

Response 200:

json
{
  "data": [
    {
      "id": "action-uuid",
      "instance_id": "instance-uuid",
      "step_id": "step-uuid",
      "user_id": "user-uuid",
      "action": "approve",
      "comment": "Looks great!",
      "data": null,
      "created_at": "2026-06-08T11:00:00Z"
    }
  ]
}

Action types:

ActionDescription
startInstance was started
approveStep was approved by a user
rejectStep was rejected by a user
delegateStep was reassigned to another user
commentComment added without state change
escalateStep was escalated due to timeout
auto_approveStep was auto-approved due to timeout
timeoutStep timed out
cancelInstance was cancelled by admin

GET /workflow-actions — All actions (admin)

Query actions across all instances.

Permission: workflow.manage

Query Parameters:

ParamDescription
instance_idFilter by instance
actionFilter by action type
limit / offsetPagination

See Also

ODP Internal API Documentation