휴먼 인풋 API 통합 흐름
휴먼 인풋 API 통합 흐름
워크플로가 Human Input 노드에 도달하면 일시정지되고, 스트리밍 응답에 human_input_required 이벤트를 띄워요. 이 이벤트에는 form_token이 실려 있는데, 워크플로가 재개될 때까지 폼 라이프사이클을 몰고 가는 역할을 해요.
출처: 공식문서
단계
아래 순서는 Workflow 앱과 Chatflow 앱 모두에 적용돼요. 1단계의 진입 엔드포인트만 둘 사이에서 달라집니다.
1단계: 스트리밍 모드로 앱 시작
-
Run Workflow(Workflow 앱) 또는 Send Chat Message(Chatflow 앱)를 호출하되, 최종 사용자의
user식별자를 넘겨요. -
SSE 스트림에서
human_input_required이벤트를 지켜보고, 그form_token을 잡아요.form_token이null이면 폼이 Email 전달 방식을 쓰는 거라 API로는 다룰 수 없어요(아래 전달 방식 요건 참고).human_input_required이벤트는 실행의workflow_run_id도 함께 실어 보내요. 5단계에서 리슨을 재개해야 할 때를 대비해 보관해 두세요.
2단계: 폼 정의 가져오기
form_token으로 Get Human Input Form을 호출해요. 응답에는 렌더링된 Markdown, 입력 필드 정의, 사용 가능한 액션, 미리 채워진 기본값, 그리고 제출 가능한 시한인 expiration_time이 포함돼요. 이 폼을 수신자에게 렌더링합니다.
제출 전에 폼이 만료되면, 일시정지된 실행은 노드 설정의 타임아웃 동작을 따르고, 재개된 스트림에는 human_input_form_filled 대신 human_input_form_timeout이 실려 와요.
3단계: (파일 입력만 해당) 로컬 파일 업로드
수신자가 file 또는 file-list 입력에 로컬 파일을 붙인다면, 먼저 Upload File로 업로드해요. 그러면 id가 돌아오는데, 제출 페이로드에서 upload_file_id로 참조해요. 실행·업로드·제출 호출 전체에 일관된 user 하나를 쓰세요(자세한 건 엔드 유저 식별자).
원격 파일은 업로드 단계가 필요 없어요. 제출 때 {transfer_method: remote_url, url} 매핑으로 인라인으로 붙이면 됩니다.
4단계: 응답 제출
수신자가 입력한 값과 선택한 action, 그리고 user를 담아 Submit Human Input Form을 호출해요. action은 2단계에서 받은 폼 정의의 액션 중 하나여야 해요.
파일 입력은 3단계의 {transfer_method: local_file, upload_file_id} 매핑 또는 인라인 {transfer_method: remote_url, url} 매핑을 받아요. 두 방식의 트레이드오프는 아래 업로드 우선 vs 인라인 원격 URL에서 설명할게요.
- 성공한 제출은 최종적이에요. 폼을 닫고 그에 맞는 액션 분기로 실행을 재개하므로, 같은
form_token으로 다시 제출할 수 없어요. - 거부된 제출(잘못된 action, 필수 입력 누락, 원격 파일 fetch 실패)은 폼을 그대로 두어요. 입력을 고쳐 같은
form_token으로 재제출하면 됩니다.
5단계: 워크플로 리슨 재개
원래 SSE 스트림이 닫혔다면 Stream Workflow Events로 다시 여는데, 1단계의 workflow_run_id와 실행을 시작한 것과 **같은 user**를 넘겨요. 다른 user면 404가 나요.
재개된 스트림에는 human_input_form_filled(제출 확인) 또는 human_input_form_timeout(폼 만료)이 담기고, 이어서 나머지 노드 이벤트가 완료까지 전달돼요. 일시정지가 없었던 실행처럼요.
include_state_snapshot=true를 추가하면 이미 실행된 노드 상태를 먼저 재생해 줘요. 워크플로에 Human Input 노드가 여러 개 순차로 있다면 기본적으로 일시정지 때마다 스트림이 닫히는데, continue_on_pause=true를 넘기면 전부를 관통하는 스트림 하나를 유지할 수 있어요.
업로드 우선 vs 인라인 원격 URL
파일 입력에는 두 패턴 모두 동작해요.
-
선업로드 후
upload_file_id참조 (권장)Upload File이 업로드 시점에 파일 크기 제한을 적용하므로, 수신자는 즉시 피드백을 받고 전체 제출을 확정하기 전에 재시도할 수 있어요.
-
transfer_method: remote_url로 인라인 제출백엔드가 제출 시점에 파일을 가져와요. 통합은 더 빠르지만, 크기·유형·fetch 실패 어느 하나라도 나면 전체 제출이 거부돼서 수신자가 다른 필드를 처음부터 다시 채워야 해요.
💡 상호작용 폼처럼 수신자 피드백이 필요한 경우엔 선업로드 패턴이 낫습니다. 이 트레이드오프는 통합이 완전히 프로그래밍 방식이고 사람이 다시 입력할 일이 없을 때만 유리해요.
전달 방식 요건
Human Input API는 Human Input 노드의 웹 앱 방식으로 전달되는 폼에서만 동작해요. Email 전용 전달은 form_token을 노출하지 않습니다.
예시: 파일 첨부 제출
이 예시는 feedback 문단 입력, attachments 파일 목록 입력, 그리고 approve/reject 액션이 있는 폼을 사용해요.
-
Get Human Input Form을 호출해 폼 정의를 받아요.
GET /form/human_input/<form_token> Authorization: Bearer ***폼 정의가 돌아옵니다.
{ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { "type": "paragraph", "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } }, { "type": "file-list", "output_variable_name": "attachments", "allowed_file_types": [ "image", "document" ], "allowed_file_extensions": [], "allowed_file_upload_methods": [ "local_file", "remote_url" ], "number_limits": 5 } ], "resolved_default_values": {}, "user_actions": [ { "id": "approve", "title": "Approve", "button_style": "primary" }, { "id": "reject", "title": "Request changes", "button_style": "default" } ], "expiration_time": 1745510400 } -
로컬 파일마다 Upload File을 호출해요.
POST /files/upload Authorization: Bearer *** Content-Type: multipart/form-data file=<binary> user=abc-123{"id": "1a77f0df-...", ...}가 돌아옵니다. -
수신자의 입력과 선택한 액션으로 Submit Human Input Form을 호출해요.
POST /form/human_input/<form_token> Authorization: Bearer *** Content-Type: application/json { "inputs": { "feedback": "Looks good to ship", "attachments": [ {"transfer_method": "local_file", "upload_file_id": "1a77f0df-..."} ] }, "action": "approve", "user": "abc-123" }{}가 돌아오고, 워크플로는approve분기를 따라 재개돼요. -
Stream Workflow Events로 실행 스트림에 재연결해 완료까지 따라가요. 위 순서의 5단계와 같습니다.