Contracts — Document Approval Engine
Integration Events
Integration Event Contract (CloudEvents)
Outbound domain events are published as CloudEvents 1.0 (FR-019), emitted via the
transactional outbox (FR-020). Delivery is at-least-once; consumers MUST dedupe on id.
Envelope attributes
| CloudEvents attribute |
Source |
specversion |
1.0 |
id |
OutboxEvent.id (UUID) — dedupe key |
source |
/wrkflw/document-approval (service/definition source URI) |
type |
event type (below) |
subject |
flow instance id |
time |
OutboxEvent.occurredAt (RFC 3339 UTC) |
datacontenttype |
application/json |
data |
type-specific payload (below) |
Event types
type |
Emitted when |
data fields |
dev.wrkflw.flow.started |
A flow instance is created (US1) |
flowId, definitionKey, documentRef, submitterId, initialState |
dev.wrkflw.task.created |
A human task is created for a stage |
flowId, taskId, stateName, candidateGroupId |
dev.wrkflw.task.claimed |
A task is claimed |
flowId, taskId, ownerId |
dev.wrkflw.task.released |
A claimed task is released |
flowId, taskId |
dev.wrkflw.decision.recorded |
A decision is recorded (US2) |
flowId, taskId, outcome, actorId, comment? |
dev.wrkflw.flow.completed |
A flow reaches a terminal outcome (US3) |
flowId, terminalOutcome |
Guarantees (SC-006)
- For every committed state change, exactly one corresponding outbox row is written in the same
transaction; it is published exactly once effectively (at-least-once delivery + consumer
dedupe on
id).
- No event is published for a rolled-back state change (the outbox row rolls back with it).
- Events are for external integration only; they never drive internal flow progression (that
is Temporal's role — Principle IV).
REST API (OpenAPI)
openapi: 3.0.0
info:
title: wrkflw Document Approval API
version: 0.1.0
description: |-
REST contract for the document-approval workflow engine.
Actor identity and group memberships are supplied via trusted headers
(X-Actor-Id, X-Actor-Groups). All command responses reflect the
authoritative DB state committed before the corresponding Temporal
signal is sent.
tags: []
paths:
/flows:
get:
operationId: FlowsApi_list
description: List flows submitted by the caller (submitter My Submissions view)
parameters: []
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/FlowSummary'
post:
operationId: FlowsApi_submit
description: Submit a document for approval — only initiator-group members may start (FR-002)
parameters: []
responses:
'201':
description: The request has succeeded and a new resource has been created as a result.
content:
application/json:
schema:
$ref: '#/components/schemas/FlowStatus'
'403':
description: Access is forbidden.
'404':
description: The server cannot find the requested resource.
'422':
description: Client error
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubmitDocumentRequest'
/flows/{flowId}:
get:
operationId: FlowInstancesApi_getStatus
description: Get flow status, pending tasks, responsible groups, and audit history
parameters:
- name: flowId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/FlowStatusWithHistory'
'404':
description: The server cannot find the requested resource.
/tasks/{taskId}/claim:
post:
operationId: TaskClaimApi_claim
description: Claim a pending task — caller must be in the candidate group and task must be PENDING (FR-008)
parameters:
- name: taskId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/TaskSummary'
'403':
description: Access is forbidden.
'404':
description: The server cannot find the requested resource.
'409':
description: The request conflicts with the current state of the server.
/tasks/{taskId}/decision:
post:
operationId: TaskDecisionApi_decide
description: Record an approve/reject decision on an owned task (FR-011, FR-012)
parameters:
- name: taskId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/FlowStatus'
'403':
description: Access is forbidden.
'404':
description: The server cannot find the requested resource.
'409':
description: The request conflicts with the current state of the server.
'422':
description: Client error
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DecisionRequest'
/tasks/{taskId}/release:
post:
operationId: TaskReleaseApi_release
description: Release a claimed task back to the group (FR-009)
parameters:
- name: taskId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
$ref: '#/components/schemas/TaskSummary'
'403':
description: Access is forbidden.
'404':
description: The server cannot find the requested resource.
'409':
description: The request conflicts with the current state of the server.
/worklists/group:
get:
operationId: GroupWorklistApi_list
description: Unclaimed tasks actionable by the caller's group(s) (FR-016)
parameters: []
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TaskSummary'
/worklists/mine:
get:
operationId: MyWorklistApi_list
description: Tasks the caller personally owns (FR-017)
parameters: []
responses:
'200':
description: The request has succeeded.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TaskSummary'
components:
schemas:
AuditEntry:
type: object
required:
- type
- actorId
- occurredAt
- detail
properties:
type:
type: string
actorId:
type: string
nullable: true
occurredAt:
type: string
format: date-time
detail:
type: object
additionalProperties: {}
DecisionOutcome:
type: string
enum:
- APPROVE
- REJECT
DecisionRequest:
type: object
required:
- outcome
properties:
outcome:
$ref: '#/components/schemas/DecisionOutcome'
comment:
type: string
FlowRunStatus:
type: string
enum:
- RUNNING
- COMPLETED
FlowStatus:
type: object
required:
- flowId
- definitionKey
- currentState
- status
- terminalOutcome
- pendingTasks
properties:
flowId:
type: string
format: uuid
definitionKey:
type: string
currentState:
type: string
status:
$ref: '#/components/schemas/FlowRunStatus'
terminalOutcome:
type: string
nullable: true
pendingTasks:
type: array
items:
$ref: '#/components/schemas/TaskSummary'
FlowStatusWithHistory:
type: object
required:
- history
properties:
history:
type: array
items:
$ref: '#/components/schemas/AuditEntry'
allOf:
- $ref: '#/components/schemas/FlowStatus'
FlowSummary:
type: object
required:
- flowId
- definitionKey
- currentState
- status
- terminalOutcome
- documentRef
- submitterId
- updatedAt
properties:
flowId:
type: string
format: uuid
definitionKey:
type: string
currentState:
type: string
status:
$ref: '#/components/schemas/FlowRunStatus'
terminalOutcome:
type: string
nullable: true
documentRef:
type: string
submitterId:
type: string
updatedAt:
type: string
format: date-time
SubmitDocumentRequest:
type: object
required:
- definitionKey
- documentRef
properties:
definitionKey:
type: string
documentRef:
type: string
description: Reference/identifier of the document
TaskRunStatus:
type: string
enum:
- PENDING
- CLAIMED
- COMPLETED
TaskSummary:
type: object
required:
- taskId
- flowId
- stateName
- candidateGroupId
- status
properties:
taskId:
type: string
format: uuid
flowId:
type: string
format: uuid
stateName:
type: string
candidateGroupId:
type: string
status:
$ref: '#/components/schemas/TaskRunStatus'
ownerId:
type: string
nullable: true
servers:
- url: /api/v1
description: Document Approval REST API
variables: {}