docs/design/human-in-the-loop/hitl-form-file-upload-design.md
HITL standalone forms can be opened directly through a form link and do not require the
submitter to sign in through the Web App. After file and file-list inputs were introduced,
this standalone entry point also needed file upload support.
This entry point has a different identity model from the existing upload paths:
EndUser context.user parameter.The goal is therefore not to add another general-purpose upload channel. The goal is to provide a constrained, short-lived upload capability that is scoped to one HITL form submission.
EndUser.HITL standalone upload uses a dedicated upload token that is bound to a form recipient. The token authorizes file upload only while the related form is still valid.
Files uploaded through the HITL standalone page are recorded under the workflow or
chatflow initiator, not under the anonymous standalone submitter and not under a technical HITL EndUser.
This keeps workflow resume aligned with the existing execution model: one initiator owns the workflow run context, and file restoration continues to resolve files through that initiator's access scope. HITL-specific form/token/file relationships remain available for audit and tracing, but they do not become the source of truth for file access control.
HITL standalone upload has three endpoint categories:
| Purpose | HITL endpoint | Aligned Web App endpoint |
|---|---|---|
| Issue upload token | POST /api/form/human_input/{form_token}/upload-token | No direct equivalent |
| Upload local file | POST /api/form/human_input/files/upload | POST /api/files/upload |
| Upload remote file | POST /api/form/human_input/files/remote-upload | POST /api/remote-files/upload |
Local upload follows the Web App POST /api/files/upload parameter shape:
multipart/form-datafileRemote upload follows the Web App POST /api/remote-files/upload parameter shape:
application/jsonurlHITL upload endpoints do not accept the Service API user parameter. That parameter
belongs to the Service API EndUser mapping model and does not represent the anonymous
standalone form submitter.
The upload token is issued through the form token:
POST /api/form/human_input/{form_token}/upload-token
Upload requests carry the token through the Authorization header:
Authorization: bearer hitl_upload_{random_value}
The hitl_upload_ prefix only distinguishes this credential from other bearer token types.
Security comes from the high-entropy random value, server-side hash storage, and server-side state validation.
The token is bound to at least:
The token must satisfy these rules:
Putting upload_token in the request body would avoid additional CORS header configuration, but it has a bad failure mode for file upload. The server often needs to parse the multipart body before it can read a body token, so invalid requests can still consume upload parsing, temporary file, memory, or disk resources.
Using Authorization: bearer hitl_upload_{random_value} keeps authentication before expensive business processing:
The tradeoff is that cross-origin deployments must allow the Authorization header and accept browser preflight requests. This is a reasonable configuration cost for an earlier and clearer authentication boundary.
The standalone submitter is not a reliable product identity. Assigning files to a technical EndUser would also conflict with workflow resume: existing file restoration expects files to be readable through the workflow or chatflow initiator's scope.
The selected model is:
Account, standalone HITL uploads are stored under that Account.EndUser, standalone HITL uploads are stored under that EndUser.This means UploadFile.created_by_role and UploadFile.created_by continue to be the source of truth for file access control. HITL association records provide auditability but do not grant file access by themselves.
The HITL upload model has two responsibilities:
These records are intentionally not tied to an EndUser. Their purpose is to preserve the HITL form/token/file relationship for audit and cleanup, not to define a separate file owner identity.
Local upload should reuse the existing file upload semantics as much as possible:
Remote upload should reuse the existing remote upload semantics as much as possible:
This is the simplest implementation option and does not require Web App login, EndUser, or a new token model. It was not selected because it exposes a public file upload surface that can be abused in SaaS or internet-facing deployments. Adding authentication later would also change the endpoint contract after clients have integrated with it.
This would maximize reuse of existing Web App upload behavior, but it would bind HITL standalone forms to Web App login, app code, Web App enablement, and enterprise SSO semantics. That coupling is undesirable because HITL forms can be reached from independent channels such as email. It also makes product behavior unclear when the Web App is disabled or the app code is reset.
EndUser From Form TokenThis would satisfy existing upload paths that require an EndUser context and would allow form state to limit upload capability. It was not selected because the created identity would be technical rather than a real submitter identity. It would also mix HITL standalone form behavior into the broader EndUser model already used by Web App, Service API, triggers, MCP, and other entry points.
More importantly, files owned by this technical EndUser would not naturally be readable through the workflow initiator scope during workflow resume.
EndUser With File Access ExceptionThis would keep the technical EndUser as the file owner and add an access-control exception so the workflow initiator can read files uploaded through the same HITL form. It solves the immediate resume problem, but it pushes a HITL-specific rule into the general file access layer. Over time, that makes permission reasoning harder and increases the chance of accidental access expansion.
Assigning files directly to the workflow or chatflow initiator avoids that bypass and keeps file access governed by the existing owner model.
EndUser.UploadFile owner as the source of truth.