Skip to main content

Overview

Workflow steps are sequential tasks within a workflow that improvers claim and complete. Each step has a specific role requirement, bounty, and work items that must be submitted.

Step Lifecycle

  • locked - Previous step not yet completed
  • available - Ready to be claimed by qualified improver
  • in_progress - Claimed and started by an improver
  • completed - Submitted and validated
  • paid_out - Improver payment processed

Claim Workflow Step

curl -X POST https://api.sfluv.com/improvers/workflows/workflow_abc123/steps/step_ghi789/claim \
  -H "Authorization: Bearer YOUR_TOKEN"
{
  "id": "workflow_abc123",
  "title": "Weekly Park Cleanup",
  "status": "in_progress",
  "steps": [
    {
      "id": "step_ghi789",
      "workflow_id": "workflow_abc123",
      "step_order": 1,
      "title": "Litter Collection",
      "bounty": 100000000000000000000,
      "role_id": "role_def456",
      "assigned_improver_id": "did:privy:improver789",
      "assigned_improver_name": "Jane Smith",
      "status": "available",
      "work_items": [...]
    }
  ]
}
Endpoint: POST /improvers/workflows/{workflow_id}/steps/{step_id}/claim Auth: Improver role required Path Parameters:
workflow_id
string
required
Workflow UUID
step_id
string
required
Step UUID
Description:
  • Assigns the step to the authenticated improver
  • Validates improver has required credentials
  • Checks for absence periods (recurring workflows)
  • Unlocks next sequential step if this is part of a chain
Response Codes:
  • 200 - Step claimed successfully
  • 400 - Step not claimable (locked, missing role, etc.)
  • 403 - Missing required credentials
  • 404 - Workflow or step not found
  • 409 - Already assigned, already claimed, or absence period conflict
  • 500 - Server error

Start Workflow Step

curl -X POST https://api.sfluv.com/improvers/workflows/workflow_abc123/steps/step_ghi789/start \
  -H "Authorization: Bearer YOUR_TOKEN"
{
  "id": "workflow_abc123",
  "status": "in_progress",
  "steps": [
    {
      "id": "step_ghi789",
      "status": "in_progress",
      "assigned_improver_id": "did:privy:improver789",
      "started_at": 1710500000,
      "work_items": [...]
    }
  ]
}
Endpoint: POST /improvers/workflows/{workflow_id}/steps/{step_id}/start Auth: Improver role required (must be assigned to step) Path Parameters:
workflow_id
string
required
Workflow UUID
step_id
string
required
Step UUID
Description: Marks the step as actively in progress. Sets started_at timestamp. Response Codes:
  • 200 - Step started successfully
  • 403 - Step not assigned to caller
  • 404 - Workflow or step not found
  • 409 - Step not available yet, not active, or already completed
  • 500 - Server error

Complete Workflow Step

curl -X POST https://api.sfluv.com/improvers/workflows/workflow_abc123/steps/step_ghi789/complete \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "step_not_possible": false,
    "items": [
      {
        "item_id": "item_jkl012",
        "photo_uploads": [
          {
            "file_name": "before.jpg",
            "content_type": "image/jpeg",
            "data_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk..."
          }
        ],
        "written_response": "Area thoroughly cleaned",
        "dropdown_value": "completed_successfully"
      }
    ]
  }'
{
  "id": "workflow_abc123",
  "status": "in_progress",
  "steps": [
    {
      "id": "step_ghi789",
      "status": "completed",
      "assigned_improver_id": "did:privy:improver789",
      "started_at": 1710500000,
      "completed_at": 1710501500,
      "submission": {
        "id": "submission_mno345",
        "workflow_id": "workflow_abc123",
        "step_id": "step_ghi789",
        "improver_id": "did:privy:improver789",
        "step_not_possible": false,
        "item_responses": [
          {
            "item_id": "item_jkl012",
            "photo_ids": ["photo_pqr678"],
            "written_response": "Area thoroughly cleaned",
            "dropdown_value": "completed_successfully"
          }
        ],
        "submitted_at": 1710501500,
        "updated_at": 1710501500
      }
    },
    {
      "id": "step_xyz999",
      "step_order": 2,
      "status": "available",
      "role_id": "role_def456"
    }
  ]
}
Endpoint: POST /improvers/workflows/{workflow_id}/steps/{step_id}/complete Auth: Improver role required (must be assigned to step) Path Parameters:
workflow_id
string
required
Workflow UUID
step_id
string
required
Step UUID
Request Body:
step_not_possible
boolean
If true, marks step as failed/impossible (must be allowed in step config)
step_not_possible_details
string
Required explanation if step_not_possible is true
items
array
required
Array of work item responses:
  • item_id (string, required): Work item UUID
  • photo_uploads (array): Photos encoded as base64
    • file_name (string): Original filename
    • content_type (string): MIME type
    • data_base64 (string): Base64-encoded image data
  • photo_ids (string[]): Previously uploaded photo IDs
  • written_response (string): Text response
  • dropdown_value (string): Selected dropdown option value
Response Behavior:
  • Validates all required work items are completed
  • Unlocks next sequential step (status changes to available)
  • Triggers email notifications for dropdown options with notify emails
  • Processes payout if workflow is completed
  • Returns updated workflow with all steps
Response Codes:
  • 200 - Step completed successfully
  • 400 - Missing required items, invalid data, or validation errors
  • 403 - Step not assigned to caller
  • 404 - Workflow or step not found
  • 500 - Server error

Request Step Payout Retry

curl -X POST https://api.sfluv.com/improvers/workflows/workflow_abc123/steps/step_ghi789/payout-request \
  -H "Authorization: Bearer YOUR_TOKEN"
{
  "id": "workflow_abc123",
  "steps": [
    {
      "id": "step_ghi789",
      "status": "completed",
      "payout_error": "insufficient balance in faucet",
      "payout_last_try_at": 1710501500,
      "retry_requested_at": 1710505000,
      "retry_requested_by": "did:privy:improver789"
    }
  ]
}
Endpoint: POST /improvers/workflows/{workflow_id}/steps/{step_id}/payout-request Auth: Improver role required (must be assigned to step) Path Parameters:
workflow_id
string
required
Workflow UUID
step_id
string
required
Step UUID
Description: Requests a retry for a failed payout. Used when step is completed but payment failed due to insufficient faucet balance or other errors. Response Codes:
  • 200 - Retry requested, payout processing attempted
  • 409 - No failed payout found for this step
  • 500 - Server error

Unclaim Workflow Series

curl -X POST https://api.sfluv.com/improvers/workflow-series/unclaim \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "series_id": "series_xyz789",
    "step_order": 1
  }'
{
  "series_id": "series_xyz789",
  "step_order": 1,
  "released_count": 3,
  "skipped_count": 1
}
Endpoint: POST /improvers/workflow-series/unclaim Auth: Improver role required Request Body:
series_id
string
required
Workflow series UUID
step_order
integer
required
Step position in workflow (1-indexed)
Description: Releases all claimed instances of a recurring workflow step in a series. Only affects future workflows (status available or locked), not in-progress or completed steps. Response:
  • released_count: Number of step assignments removed
  • skipped_count: Number of steps that couldn’t be released (already started/completed)
Response Codes:
  • 200 - Unclaim successful
  • 400 - Invalid series ID or step order
  • 409 - No claimed recurring workpiece found
  • 500 - Server error

Schema Reference

WorkflowStep Object

interface WorkflowStep {
  id: string
  workflow_id: string
  step_order: number  // 1-indexed position
  title: string
  description: string
  bounty: number  // wei (uint64)
  allow_step_not_possible: boolean
  role_id?: string | null
  assigned_improver_id?: string | null
  assigned_improver_name?: string | null
  status: "locked" | "available" | "in_progress" | "completed" | "paid_out"
  started_at?: number | null
  completed_at?: number | null
  payout_error?: string | null
  payout_last_try_at?: number | null
  retry_requested_at?: number | null
  retry_requested_by?: string | null
  submission?: WorkflowStepSubmission | null
  work_items: WorkflowWorkItem[]
}

WorkflowStepSubmission Object

interface WorkflowStepSubmission {
  id: string
  workflow_id: string
  step_id: string
  improver_id: string
  step_not_possible: boolean
  step_not_possible_details?: string | null
  item_responses: WorkflowStepItemResponse[]
  submitted_at: number  // Unix timestamp
  updated_at: number
}

WorkflowStepItemResponse Object

interface WorkflowStepItemResponse {
  item_id: string
  photo_urls?: string[]  // legacy
  photo_ids?: string[]
  photo_uploads?: WorkflowPhotoUpload[]
  photos?: WorkflowSubmissionPhoto[]
  written_response?: string
  dropdown_value?: string
}

WorkflowWorkItem Object

interface WorkflowWorkItem {
  id: string
  step_id: string
  item_order: number
  title: string
  description: string
  optional: boolean
  requires_photo: boolean
  camera_capture_only: boolean
  photo_required_count: number
  photo_allow_any_count: boolean
  photo_aspect_ratio: "vertical" | "square" | "horizontal"
  requires_written_response: boolean
  requires_dropdown: boolean
  dropdown_options: WorkflowDropdownOption[]
  dropdown_requires_written_response: Record<string, boolean>
}

WorkflowDropdownOption Object

interface WorkflowDropdownOption {
  value: string  // unique identifier
  label: string  // display text
  requires_written_response: boolean
  notify_emails?: string[]  // emails to notify when selected (redacted for non-managers)
  notify_email_count?: number  // count visible to all users
}

WorkflowPhotoUpload Object

interface WorkflowPhotoUpload {
  file_name: string
  content_type: string  // e.g. "image/jpeg"
  data_base64: string  // base64-encoded image data
}

WorkflowSubmissionPhoto Object

interface WorkflowSubmissionPhoto {
  id: string
  workflow_id: string
  step_id: string
  item_id: string
  submission_id: string
  file_name: string
  content_type: string
  size_bytes: number
  created_at: number
}

Build docs developers (and LLMs) love