# Asynchronous File Processing with WebSocket Feedback (Ticket Pattern) This example shows why WebSocket is useful for long-running file processing, without uploading files over WebSocket. ## Problem * Uploading files over WebSocket is not the intended workflow. * File upload happens over HTTP (REST). * Processing can be asynchronous and may finish quickly. If the client uploads first and subscribes later, it can miss events when processing finishes quickly. ## Solution: ticket-first workflow Before uploading, the client asks over WebSocket for a **ticket** that binds: * the upcoming HTTP upload * the processing job * the WebSocket subscription The client then registers **before** uploading, using the ticket as a filter. **Important:** you only receive server-emitted status updates if you have **registered the corresponding event resources** (via `register`). If you want updates for multiple stage commands, register them all (or design a single aggregated command that you register once). **Important:** when the server responds to a client command, it replies with the original event fields, camelCase-transformed, and adds a nested `response` object. There is no top-level `status`; status is inside `response.status`. ## Flow 1. WS: request a **document-ticket** (creates a processing correlation ID) 2. WS: register for status events filtered by that ticket (required) 3. HTTP: upload the file and include the ticket 4. WS: receive stage-specific processing events (`document-preparing`, `document-ocr`, `document-ready`) --- ## 1) WS: request a ticket Client: ```json { "command": "document-ticket" } ``` Server: ```json { "command": "document-ticket", "response": { "data": { "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a" }, "status": "OK" } } ``` --- ## 2) WS: register before upload (required) Client: ```json { "command": "register", "event": "document-ready", "param_set": { "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a" } } ``` Server replies with the original event fields (camelCase-transformed) and a nested `response` object: ```json { "command": "register", "event": "document-ready", "paramSet": { "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a" }, "response": { "status": "OK" } } ``` --- ## 3) HTTP: upload with ticket Upload the file over REST and include the ticket so the server can bind the processing job to your WS subscription. Pick one convention in your application and document it: * header * query parameter * form field --- ## 4) WS: receive status events Server pushes use the same envelope: After the upload is accepted, the server emits stage-specific commands as processing progresses (for example `document-preparing`, `document-ocr`, and finally `document-ready`). During file handling, the server may emit **different commands** for different stages. ### Example: preparing (server accepted upload and queued work) ```json { "command": "document-preparing", "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a", "response": { "status": "OK" } } ``` ### Example: OCR started ```json { "command": "document-ocr", "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a", "response": { "data": { "progress": null }, "status": "OK" } } ``` ### Example: OCR progress `document-ocr` may be emitted multiple times during processing. For example, it can be sent on every progress update (e.g. 0–100%), potentially dozens or hundreds of times. ```json { "command": "document-ocr", "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a", "response": { "data": { "progress": 65 }, "status": "OK" } } ``` ### Example: extraction finished ```json { "command": "document-ready", "ticket": "c3b7b2c0-7b9b-4c5d-8b52-1f94d98b9e2a", "response": { "data": { "documentId": 123 }, "status": "OK" } } ``` This pattern allows the client to: * show real-time progress per stage * react immediately when processing completes * avoid polling entirely