Example: Document Submission Workflow¶
This walkthrough shows how to build a workflow that asks a user to download a form, fill it out, upload the completed version, and route it through an approval before the workflow proceeds.
Use Case¶
An HR onboarding process requires a new employee to complete and submit a signed I-9 form. An HR manager must review and approve the submission before the employee's onboarding continues.
Prerequisites¶
- A document template (blank I-9 PDF) uploaded via Document Templates (
POST /api/document-templates). - A workflow variable
hrManagerIdof type String containing the HR manager's user ID.
Workflow Settings¶
| Field | Value |
|---|---|
| Category | user |
| Subject Variable | user (auto-created) |
| Error Strategy | stop |
| Trigger | manual |
When you select the user category, the designer automatically creates a context variable named user of type User and selects it as the subject variable. Every run is linked to the employee being onboarded. This allows administrators to see all document submission activity for a person via GET /api/users/:id/workflow-activity or filter runs with GET /api/runs?subjectType=user&subjectId=....
Workflow Definition¶
| Step | Type | Purpose |
|---|---|---|
| Start | start |
Entry point |
| Submit Signed I-9 | document_submission |
Sends the employee a blank I-9, waits for upload, then routes to HR for approval |
| I-9 Approved | notification |
Sends confirmation email on approval |
| I-9 Rejected | notification |
Sends rejection email and instructions to resubmit |
| End | end |
Exit point |
Step Configuration¶
Submit Signed I-9¶
{
"id": "request-i9",
"name": "Submit Signed I-9",
"type": "document_submission",
"config": {
"submitterEmail": "{{user.email}}",
"submitterId": "{{user.id}}",
"documentLabel": "Signed I-9 Form",
"templateId": "<uuid-of-blank-i9-template>",
"instructions": "Download the I-9 form below, complete and sign it, then upload the signed copy as a PDF.",
"allowedMimeTypes": ["application/pdf"],
"maxSizeBytes": 5242880,
"approvers": ["{{hrManagerId}}"],
"notifySubmitter": true,
"notifyApprovers": true
},
"transitions": [
{ "on": "success", "goto": "i9-approved" },
{ "on": "error", "goto": "i9-rejected" }
]
}
Key config fields:
- templateId — references the blank I-9 PDF uploaded to Document Templates. The submitter sees a "Download Form" button.
- allowedMimeTypes — restricts uploads to PDFs only.
- maxSizeBytes — limits file size to 5 MB.
- approvers — the HR manager reviews the uploaded document.
- notifySubmitter / notifyApprovers — sends email notifications at each phase.
Transitions¶
- success — all approvers approved the document. Routes to the confirmation notification.
- error — any approver rejected the document. Routes to the rejection notification.
Runtime Flow¶
Start
│
▼
Submit Signed I-9 (document_submission)
│
├── Engine sends notification to employee with "Download Form" link
├── Run pauses (waiting_submission)
│
│ [Employee downloads blank I-9, fills it out, uploads signed PDF]
│ [Submitter may include optional comments with their upload]
│
├── Upload creates approval records for HR manager
├── Task transitions to waiting_approval
│
│ [Employee can WITHDRAW submission → returns to waiting_submission]
│
│ [HR manager reviews document, approves or rejects]
│
├── approved ──► I-9 Approved (notification) ──► End
└── rejected ──► Task resets to waiting_submission
[Employee sees rejection reason, resubmits]
──► Back to waiting_approval (loop)
Step-by-step¶
- The workflow starts and reaches the Submit Signed I-9 step.
- The engine sends a notification email to the employee and pauses the run with status
waiting_submission. - The employee logs in, sees the task in My Tasks, clicks Download Form to get the blank I-9, completes it, and clicks Upload Document to submit the signed PDF. They may optionally add comments for the reviewer.
- The system validates the file type and size, stores the document, and creates an approval record for the HR manager. The task transitions to
waiting_approval. - While pending approval, the employee can withdraw the submission. This cancels pending approvals, marks the document as
withdrawn, and resets the task towaiting_submissionso they can upload a corrected file. - The HR manager sees a pending approval in My Tasks > Approvals, clicks to review, downloads the submitted document, and approves or rejects. A rejection reason is required.
- If approved: the task completes with
success, the workflow advances (e.g., sends a confirmation email), and the run continues. - If rejected: the task resets to
waiting_submission. The employee sees the rejection reason and can upload a revised document. This cycle repeats until the document is approved (or an admin manually fails/skips the step).
Document Lifecycle¶
| Status | Meaning |
|---|---|
uploaded |
Document submitted, pending approval |
approved |
All approvers approved |
rejected |
An approver rejected; submitter may resubmit |
withdrawn |
Submitter withdrew before approval decision |
cancelled |
Parent run was cancelled |
expired |
Approved document past its expiry date |
Withdrawal¶
A submitter (or admin) can withdraw a document while it is pending approval via POST /api/documents/:id/withdraw. This:
- Sets the document status to
withdrawn - Cancels all pending approval records for the step
- Resets the task to
waiting_submission
Submitter Comments¶
When uploading a document, the submitter can include a submitterComments field in the multipart form data. These comments are stored on the document record and visible to approvers during review.
Rejection and Resubmission¶
When an approver rejects a document, they must provide a rejection reason. The rejection:
- Sets the document status to
rejectedand stores the rejection reason - Resets the task to
waiting_submission(does not advance the workflow) - The submitter sees the rejection reason in their task inbox and can upload a revised document
- A new approval cycle starts with the resubmitted document
Variations¶
No Approval Required¶
Remove the approvers field. The document is accepted immediately upon upload and the workflow continues.
Multiple Approvers¶
Provide multiple user IDs in approvers. All must approve for the step to succeed; if any rejects, the step fails.
External Template URL¶
Instead of uploading a template to Document Templates, use templateUrl to link to an externally-hosted form:
The submitter sees an "Open Form" link that opens in a new tab.
Training Certificate Upload (No Template)¶
Omit both templateId and templateUrl. The step simply asks the user to upload a document: