SWF
Protocol: JSON 1.0 (X-Amz-Target: SimpleWorkflowService.*)
Endpoint: POST http://localhost:4566/
Domains, workflow and activity type registration, and the workflow execution state machine: decision tasks, activity tasks, timers, signals, child workflows, and timeouts.
Supported Actions
| Action | Description |
|---|---|
RegisterDomain |
Create a domain with a workflow-execution retention period |
DescribeDomain |
Get a domain's status, description, ARN, and retention configuration |
ListDomains |
List domains by registration status |
DeprecateDomain |
Deprecate a domain, blocking new registrations and executions |
UndeprecateDomain |
Return a deprecated domain to REGISTERED |
RegisterWorkflowType |
Register a workflow type and its execution defaults |
DescribeWorkflowType |
Get a workflow type's status and registered defaults |
ListWorkflowTypes |
List workflow types by registration status, optionally by name |
DeprecateWorkflowType |
Deprecate a workflow type, blocking new executions |
UndeprecateWorkflowType |
Return a deprecated workflow type to REGISTERED |
DeleteWorkflowType |
Delete a deprecated workflow type |
RegisterActivityType |
Register an activity type and its task timeout defaults |
DescribeActivityType |
Get an activity type's status and registered defaults |
ListActivityTypes |
List activity types by registration status, optionally by name |
DeprecateActivityType |
Deprecate an activity type, blocking new task scheduling |
UndeprecateActivityType |
Return a deprecated activity type to REGISTERED |
DeleteActivityType |
Delete a deprecated activity type |
StartWorkflowExecution |
Start an execution, resolving unset fields from the type defaults |
DescribeWorkflowExecution |
Get an execution's status, configuration, and open task counts |
GetWorkflowExecutionHistory |
Read an execution's history, paginated and optionally reversed |
ListOpenWorkflowExecutions |
List open executions with execution, type, tag, and time filters |
ListClosedWorkflowExecutions |
List closed executions, additionally filtered by close status |
CountOpenWorkflowExecutions |
Count open executions matching the same filters |
CountClosedWorkflowExecutions |
Count closed executions matching the same filters |
CountPendingActivityTasks |
Count activity tasks awaiting a worker on a task list |
CountPendingDecisionTasks |
Count decision tasks awaiting a decider on a task list |
PollForDecisionTask |
Claim a decision task with the execution history the decider needs |
RespondDecisionTaskCompleted |
Apply a decider's decisions in order |
PollForActivityTask |
Claim an activity task with its input |
RecordActivityTaskHeartbeat |
Report progress and learn whether cancellation was requested |
RespondActivityTaskCompleted |
Complete an activity task with a result |
RespondActivityTaskFailed |
Fail an activity task with a reason and details |
RespondActivityTaskCanceled |
Confirm an activity task was canceled |
SignalWorkflowExecution |
Deliver a signal to an open execution |
RequestCancelWorkflowExecution |
Ask an execution's decider to cancel it |
TerminateWorkflowExecution |
Close an execution immediately, applying its child policy |
ListTagsForResource |
List a domain's tags |
TagResource |
Add tags to a domain |
UntagResource |
Remove tags from a domain |
All 39 modeled operations are implemented.
Decisions
RespondDecisionTaskCompleted applies decisions in order:
| Decision | Behavior |
|---|---|
ScheduleActivityTask |
Schedules an activity task; timeouts fall back to the activity type's registration defaults |
RequestCancelActivityTask |
Requests cancellation; a task that has not started yet is canceled immediately |
CompleteWorkflowExecution |
Closes the execution as COMPLETED |
FailWorkflowExecution |
Closes the execution as FAILED |
CancelWorkflowExecution |
Closes the execution as CANCELED |
ContinueAsNewWorkflowExecution |
Closes the run as CONTINUED_AS_NEW and starts a successor run |
RecordMarker |
Appends MarkerRecorded |
StartTimer / CancelTimer |
Starts or cancels a timer; a due timer fires TimerFired |
SignalExternalWorkflowExecution |
Delivers a signal to another execution in the same domain |
RequestCancelExternalWorkflowExecution |
Requests cancellation of another execution |
StartChildWorkflowExecution |
Starts a child execution and reports its outcome back to the parent |
ScheduleLambdaFunction |
Invokes the function through Floci's Lambda service and records its result (see Lambda functions) |
A decision that cannot be applied appends its *Failed event with the AWS cause
(ACTIVITY_TYPE_DOES_NOT_EXIST, ACTIVITY_ID_ALREADY_IN_USE, TIMER_ID_UNKNOWN,
UNHANDLED_DECISION, …) instead of failing the request, and the decider is given a
fresh decision task. A closing decision must be last in the batch: a batch with anything
after it is rejected with ValidationException ("Close must be last decision in list") and
no decision is applied.
Timeouts
A background sweep expires timeouts and appends the matching event:
| Timeout | Event | timeoutType |
|---|---|---|
| Activity schedule-to-start | ActivityTaskTimedOut |
SCHEDULE_TO_START |
| Activity start-to-close | ActivityTaskTimedOut |
START_TO_CLOSE |
| Activity schedule-to-close | ActivityTaskTimedOut |
SCHEDULE_TO_CLOSE |
| Activity heartbeat | ActivityTaskTimedOut |
HEARTBEAT |
| Decision task start-to-close | DecisionTaskTimedOut |
START_TO_CLOSE |
| Workflow execution start-to-close | WorkflowExecutionTimedOut |
START_TO_CLOSE |
Any timeout other than the workflow's own schedules a new decision task. The literal
NONE disables a timeout, as in AWS.
Child policy
When an execution closes, its childPolicy is applied to still-open children:
TERMINATE terminates them with cause CHILD_POLICY_APPLIED, REQUEST_CANCEL requests
cancellation, and ABANDON leaves them running.
Lambda functions
ScheduleLambdaFunction runs a real function: Floci invokes it through its own Lambda
service, which executes it in a container exactly as an Invoke API call would, and records
the outcome in history. Deploy the function with the Lambda API first — SWF resolves it by
name in the region its domain was registered in.
The execution needs a lambdaRole, taken from StartWorkflowExecution's lambdaRole or,
when absent, the workflow type's defaultLambdaRole. The role is not evaluated as a policy;
its presence is what SWF requires in order to invoke.
| Outcome | History |
|---|---|
| Success | LambdaFunctionScheduled → LambdaFunctionStarted → LambdaFunctionCompleted with the response as result |
| Handler raised | …Scheduled → …Started → LambdaFunctionFailed with reason Handled/Unhandled and the error payload as details |
| Function not found | …Scheduled → …Started → LambdaFunctionFailed with reason ResourceNotFoundException |
No lambdaRole |
…Scheduled → StartLambdaFunctionFailed with cause ASSUME_ROLE_FAILED; the function is never started |
A decision with no input delivers {} to the handler, and reusing an id is accepted —
SWF does not report ID_ALREADY_IN_USE for Lambda invocations the way it does for activity
ids. Every outcome schedules a fresh decision task so the decider can react.
Regions
SWF names are unique per region, so all state is region-scoped: registering orders in
us-east-1 and again in eu-west-1 creates two independent domains, each with its own
description, retention period, and ARN. Types, executions, and task tokens follow the same
rule — a runId from one region does not resolve in another, ListDomains and the type
listings only report the caller's region, and a decision or activity task is only handed to a
poller in the region that owns it.
Configuration
| Setting | Env var | Default | Description |
|---|---|---|---|
enabled |
FLOCI_SERVICES_SWF_ENABLED |
true |
Enable the service |
timeout-sweep-enabled |
FLOCI_SERVICES_SWF_TIMEOUT_SWEEP_ENABLED |
true |
Run the background timeout sweep |
timeout-sweep-interval-seconds |
FLOCI_SERVICES_SWF_TIMEOUT_SWEEP_INTERVAL_SECONDS |
1 |
Sweep interval |
Example
Register a domain and the types an execution needs:
aws --endpoint-url http://localhost:4566 swf register-domain \
--name orders --workflow-execution-retention-period-in-days 7
aws --endpoint-url http://localhost:4566 swf register-workflow-type \
--domain orders --name OrderWorkflow --workflow-version 1.0 \
--default-task-list name=orders-tl \
--default-task-start-to-close-timeout 60 \
--default-execution-start-to-close-timeout 3600 \
--default-child-policy TERMINATE
aws --endpoint-url http://localhost:4566 swf register-activity-type \
--domain orders --name ChargeCard --activity-version 1.0 \
--default-task-list name=orders-tl \
--default-task-schedule-to-start-timeout 60 \
--default-task-start-to-close-timeout 300
Start an execution:
aws --endpoint-url http://localhost:4566 swf start-workflow-execution \
--domain orders --workflow-id order-1234 \
--workflow-type name=OrderWorkflow,version=1.0 \
--input '{"orderId":"1234"}'
Poll as a decider and schedule the activity:
aws --endpoint-url http://localhost:4566 swf poll-for-decision-task \
--domain orders --task-list name=orders-tl --identity decider-1 > task.json
TASK_TOKEN=$(jq -r .taskToken task.json)
cat > decisions.json <<'EOF'
[{"decisionType": "ScheduleActivityTask",
"scheduleActivityTaskDecisionAttributes": {
"activityId": "charge-1",
"activityType": {"name": "ChargeCard", "version": "1.0"},
"input": "{\"amount\":4200}"}}]
EOF
aws --endpoint-url http://localhost:4566 swf respond-decision-task-completed \
--task-token "$TASK_TOKEN" --decisions file://decisions.json
Poll as a worker and complete the activity:
aws --endpoint-url http://localhost:4566 swf poll-for-activity-task \
--domain orders --task-list name=orders-tl --identity worker-1 > activity.json
ACTIVITY_TOKEN=$(jq -r .taskToken activity.json)
aws --endpoint-url http://localhost:4566 swf respond-activity-task-completed \
--task-token "$ACTIVITY_TOKEN" --result '{"charged":true}'
Read the history:
aws --endpoint-url http://localhost:4566 swf get-workflow-execution-history \
--domain orders --execution workflowId=order-1234,runId=<runId>
Errors
Faults use the AWS codes and messages, so SDK error handling works unchanged:
| Fault | Raised by |
|---|---|
UnknownResourceFault |
Unknown domain, type, execution, or task token; also an activity task token whose task has already closed, reported as Unknown activity, scheduledEventId = N |
DomainAlreadyExistsFault |
RegisterDomain for an existing domain; UndeprecateDomain on a registered domain |
DomainDeprecatedFault |
Registering into, or starting an execution in, a deprecated domain |
TypeAlreadyExistsFault |
Registering an existing type; undeprecating a registered type |
TypeDeprecatedFault |
Starting an execution of a deprecated workflow type |
TypeNotDeprecatedFault |
DeleteWorkflowType / DeleteActivityType before deprecation |
WorkflowExecutionAlreadyStartedFault |
StartWorkflowExecution while an execution with that workflowId is open |
DefaultUndefinedFault |
A required field is absent from both the request and the type's defaults |
TooManyTagsFault |
More than 50 tags on a domain |
ValidationException |
Missing required member, a value outside an enum, or a closing decision that is not last |
Limitations
- Long polling.
PollForDecisionTaskandPollForActivityTaskreturn immediately instead of holding the connection for up to 60 seconds. Workers that loop on an empty poll behave the same; workers that rely on the call blocking will spin. - Task tokens are in-memory. Tokens do not survive a restart even under persistent storage modes, matching the fact that SWF tokens are not durable handles.
- Retention.
workflowExecutionRetentionPeriodInDaysis stored and returned but closed executions are not pruned. - Pagination.
maximumPageSizeandnextPageTokenare honored on every paginated operation — the history, the execution listings, the registration listings (ListDomains,ListWorkflowTypes,ListActivityTypes) andPollForDecisionTask. All of them apply the service's 1000 cap, rejecting a larger value rather than clamping it. Tokens are opaque and are not interchangeable with real SWF tokens. PollForDecisionTaskcontinuation. A request carryingnextPageTokencontinues the earlier poll instead of claiming a new task, so it returns the samestartedEventId,previousStartedEventIdandworkflowExecutionwith the next page of that task's history.startAtPreviousStartedEventtrims the window to events frompreviousStartedEventIdonward, which the service treats as inclusive.- Decision batches are all-or-nothing. A batch containing an unknown
decisionType, or a closing decision that is not last, is rejected withValidationExceptionbefore anything is applied: noDecisionTaskCompleted, no earlier decision, and the task token stays claimable for a corrected batch.