{"id": "Qwbp3muM7gA", "title": "How to upload videos to the vending machine", "formats": [{"format_id": "sb1", "format_note": "storyboard", "ext": "mhtml", "protocol": "mhtml", "acodec": "none", "vcodec": "none", "url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L1/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLDiNyPgbTqv2Uye2vjlkTBg1Wb9zA", "width": 25, "height": 45, "fps": 1.0123456790123457, "rows": 10, "columns": 10, "fragments": [{"url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L1/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLDiNyPgbTqv2Uye2vjlkTBg1Wb9zA", "duration": 81.0}], "audio_ext": "none", "video_ext": "none", "vbr": 0, "abr": 0, "tbr": null, "resolution": "25x45", "aspect_ratio": 0.56, "filesize_approx": null, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "sb1 - 25x45 (storyboard)"}, {"format_id": "sb2", "format_note": "storyboard", "ext": "mhtml", "protocol": "mhtml", "acodec": "none", "vcodec": "none", "url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBOfgYfhCfULBEtoVhfdhqWt9CVow", "width": 48, "height": 27, "fps": 1.2345679012345678, "rows": 10, "columns": 10, "fragments": [{"url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L0/default.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBOfgYfhCfULBEtoVhfdhqWt9CVow", "duration": 81.0}], "audio_ext": "none", "video_ext": "none", "vbr": 0, "abr": 0, "tbr": null, "resolution": "48x27", "aspect_ratio": 1.78, "filesize_approx": null, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "sb2 - 48x27 (storyboard)"}, {"format_id": "sb0", "format_note": "storyboard", "ext": "mhtml", "protocol": "mhtml", "acodec": "none", "vcodec": "none", "url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L2/M$M.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBg_VjDB7EHbpMA2cIVdqU5iU2QSg", "width": 50, "height": 90, "fps": 1.0123456790123457, "rows": 5, "columns": 5, "fragments": [{"url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L2/M0.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBg_VjDB7EHbpMA2cIVdqU5iU2QSg", "duration": 24.695121951219512}, {"url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L2/M1.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBg_VjDB7EHbpMA2cIVdqU5iU2QSg", "duration": 24.695121951219512}, {"url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L2/M2.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBg_VjDB7EHbpMA2cIVdqU5iU2QSg", "duration": 24.695121951219512}, {"url": "https://i.ytimg.com/sb/Qwbp3muM7gA/storyboard3_L2/M3.jpg?sqp=-oaymwENSDfyq4qpAwVwAcABBqLzl_8DBgiB9KqOBg==&sigh=rs$AOn4CLBg_VjDB7EHbpMA2cIVdqU5iU2QSg", "duration": 6.914634146341456}], "audio_ext": "none", "video_ext": "none", "vbr": 0, "abr": 0, "tbr": null, "resolution": "50x90", "aspect_ratio": 0.56, "filesize_approx": null, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "sb0 - 50x90 (storyboard)"}, {"format_id": "233", "format_note": "Default, low", "format_index": null, "url": "https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/itag/233/source/youtube/requiressl/yes/ratebypass/yes/pfa/1/goi/133/sgoap/clen%3D493399%3Bdur%3D80.712%3Bgir%3Dyes%3Bitag%3D139%3Blmt%3D1723765237342367/rqh/1/hls_chunk_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/xpc/EgVo2aDSNQ%3D%3D/cps/198/met/1777576737,/mh/Vu/mm/31,26/mn/sn-uxaxjvh5gbxoupo5-x1xll,sn-nja7snll/ms/au,onr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au,au/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/playlist_type/DVR/dover/13/txp/6218224/mt/1777576422/fvip/5/short_key/1/keepalive/yes/fexp/51565115,51565682/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,goi,sgoap,rqh,xpc,bui,spc,vprv,playlist_type/sig/AHEqNM4wRQIhAMM4jiXmofwx62A1HfYSvbAHl_O4AdlL_VWYzzfQe4rnAiA7jMLmrPHo89XxXAhuzN6v4jzxf1DYSCUKaLuTrz2a1A%3D%3D/lsparams/hls_chunk_host,cps,met,mh,mm,mn,ms,mv,mvi,pcm2cms,pl,rms,initcwndbps/lsig/APaTxxMwRAIgfd8j7tGOtG6EFfIAsRh7rtphkfHkwMtsI3n8JT-iMnECIAvfbVX0FalWJUjQx1zqj9wasY5D4FbTHLbl7GeoRls5/playlist/index.m3u8", "manifest_url": "https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/source/youtube/requiressl/yes/xpc/EgVo2aDSNQ%3D%3D/playback_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/cps/198/met/1777576737%2C/mh/Vu/mm/31%2C26/mn/sn-uxaxjvh5gbxoupo5-x1xll%2Csn-nja7snll/ms/au%2Conr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au%2Cau/tx/51773318/txs/51773317%2C51773318/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/go/1/rqh/5/mt/1777576422/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/51565115%2C51565682/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Cxpc%2Ctx%2Ctxs%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cbui%2Cspc%2Cvprv%2Cgo%2Crqh%2Citag%2Cplaylist_type/sig/AHEqNM4wRAIgKUmkyhslyYkPR-KL1dL_DtdGyLwNqUAmWkAylFX_A8UCIGOJ53oQe7nXd65HNztTuPCMRNXAO9w_6bYjMZQdxkih/lsparams/playback_host%2Ccps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpcm2cms%2Cpl%2Crms%2Cinitcwndbps/lsig/APaTxxMwRQIhAOovFXbPNaBZju3WBZnb9webS_PtZIyIeNcD6cOPz4BeAiA8OhPY1AytplhSixn8eTJhyeycq0hLy9J3_pL_0pAgTg%3D%3D/file/index.m3u8", "language": null, "ext": "mp4", "protocol": "m3u8_native", "preference": null, "quality": -1, "has_drm": false, "vcodec": "none", "source_preference": 0, "audio_ext": "mp4", "video_ext": "none", "vbr": 0, "abr": null, "tbr": null, "resolution": "audio only", "aspect_ratio": null, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "233 - audio only (Default, low)"}, {"format_id": "234", "format_note": "Default, high", "format_index": null, "url": "https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/itag/234/source/youtube/requiressl/yes/ratebypass/yes/pfa/1/goi/133/sgoap/clen%3D1306118%3Bdur%3D80.642%3Bgir%3Dyes%3Bitag%3D140%3Blmt%3D1723765237365164/rqh/1/hls_chunk_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/xpc/EgVo2aDSNQ%3D%3D/cps/198/met/1777576737,/mh/Vu/mm/31,26/mn/sn-uxaxjvh5gbxoupo5-x1xll,sn-nja7snll/ms/au,onr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au,au/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/playlist_type/DVR/dover/13/txp/6218224/mt/1777576422/fvip/5/short_key/1/keepalive/yes/fexp/51565115,51565682/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,goi,sgoap,rqh,xpc,bui,spc,vprv,playlist_type/sig/AHEqNM4wRAIgMKj_RDyWEUUPU9d5fZcELyu7NHrPIsflg7xLNPQDa58CIGndJmXsytUoVO9B6RB9LwTyotpZYuUjvHoboiOhV4xv/lsparams/hls_chunk_host,cps,met,mh,mm,mn,ms,mv,mvi,pcm2cms,pl,rms,initcwndbps/lsig/APaTxxMwRgIhAKZO_OyMS2Kepy1KBTpuE6QzQG9GKGwUlaQbq0CMEfYjAiEA8DsYGBm5cFzBc459qS0wrzPHIR7O7gC8Um8cfqjUE94%3D/playlist/index.m3u8", "manifest_url": "https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/source/youtube/requiressl/yes/xpc/EgVo2aDSNQ%3D%3D/playback_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/cps/198/met/1777576737%2C/mh/Vu/mm/31%2C26/mn/sn-uxaxjvh5gbxoupo5-x1xll%2Csn-nja7snll/ms/au%2Conr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au%2Cau/tx/51773318/txs/51773317%2C51773318/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/go/1/rqh/5/mt/1777576422/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/51565115%2C51565682/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Cxpc%2Ctx%2Ctxs%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cbui%2Cspc%2Cvprv%2Cgo%2Crqh%2Citag%2Cplaylist_type/sig/AHEqNM4wRAIgKUmkyhslyYkPR-KL1dL_DtdGyLwNqUAmWkAylFX_A8UCIGOJ53oQe7nXd65HNztTuPCMRNXAO9w_6bYjMZQdxkih/lsparams/playback_host%2Ccps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpcm2cms%2Cpl%2Crms%2Cinitcwndbps/lsig/APaTxxMwRQIhAOovFXbPNaBZju3WBZnb9webS_PtZIyIeNcD6cOPz4BeAiA8OhPY1AytplhSixn8eTJhyeycq0hLy9J3_pL_0pAgTg%3D%3D/file/index.m3u8", "language": null, "ext": "mp4", "protocol": "m3u8_native", "preference": null, "quality": -1, "has_drm": false, "vcodec": "none", "source_preference": 1, "audio_ext": "mp4", "video_ext": "none", "vbr": 0, "abr": null, "tbr": null, "resolution": "audio only", "aspect_ratio": null, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "234 - audio only (Default, high)"}, {"format_id": "602", "format_index": null, "url": "https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/itag/602/source/youtube/requiressl/yes/ratebypass/yes/pfa/1/wft/1/sgovp/clen%3D309610%3Bdur%3D80.599%3Bgir%3Dyes%3Bitag%3D598%3Blmt%3D1723765250415530/rqh/1/hls_chunk_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/xpc/EgVo2aDSNQ%3D%3D/cps/198/met/1777576737,/mh/Vu/mm/31,26/mn/sn-uxaxjvh5gbxoupo5-x1xll,sn-nja7snll/ms/au,onr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au,au/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/playlist_type/DVR/dover/13/txp/6219224/mt/1777576422/fvip/5/short_key/1/keepalive/yes/fexp/51565115,51565682/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,wft,sgovp,rqh,xpc,bui,spc,vprv,playlist_type/sig/AHEqNM4wRgIhAM1ZBsJ9qA-ibzN3Vruj-KIORypgO8bpzQIl1iiIduUaAiEAttvr-SZKXed8141A-qu7D_xzEkDS0FgTNKykQWNblEg%3D/lsparams/hls_chunk_host,cps,met,mh,mm,mn,ms,mv,mvi,pcm2cms,pl,rms,initcwndbps/lsig/APaTxxMwRQIgZtF7CCbC5xRbDtxaI-KS161xWbChxn2SW1OH4W-rZIcCIQCIaQJ6sfOBINiMOxYSZDuAOHBny02saLRFYNADb6ixUA%3D%3D/playlist/index.m3u8", "manifest_url": "https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/source/youtube/requiressl/yes/xpc/EgVo2aDSNQ%3D%3D/playback_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/cps/198/met/1777576737%2C/mh/Vu/mm/31%2C26/mn/sn-uxaxjvh5gbxoupo5-x1xll%2Csn-nja7snll/ms/au%2Conr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au%2Cau/tx/51773318/txs/51773317%2C51773318/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/go/1/rqh/5/mt/1777576422/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/51565115%2C51565682/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Cxpc%2Ctx%2Ctxs%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cbui%2Cspc%2Cvprv%2Cgo%2Crqh%2Citag%2Cplaylist_type/sig/AHEqNM4wRAIgKUmkyhslyYkPR-KL1dL_DtdGyLwNqUAmWkAylFX_A8UCIGOJ53oQe7nXd65HNztTuPCMRNXAO9w_6bYjMZQdxkih/lsparams/playback_host%2Ccps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpcm2cms%2Cpl%2Crms%2Cinitcwndbps/lsig/APaTxxMwRQIhAOovFXbPNaBZju3WBZnb9webS_PtZIyIeNcD6cOPz4BeAiA8OhPY1AytplhSixn8eTJhyeycq0hLy9J3_pL_0pAgTg%3D%3D/file/index.m3u8", "tbr": 91.035, "ext": "mp4", "fps": 15.0, "protocol": "m3u8_native", "preference": null, "quality": 0, "has_drm": false, "width": 144, "height": 256, "vcodec": "vp09.00.10.08", "acodec": "none", "dynamic_range": "SDR", "source_preference": -1, "video_ext": "mp4", "audio_ext": "none", "abr": 0, "vbr": 91.035, "resolution": "144x256", "aspect_ratio": 0.56, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "602 - 144x256"}, {"format_id": "269", "format_index": null, "url": "https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/itag/269/source/youtube/requiressl/yes/ratebypass/yes/pfa/1/sgovp/clen%3D1127038%3Bdur%3D80.566%3Bgir%3Dyes%3Bitag%3D160%3Blmt%3D1723765569094925/rqh/1/hls_chunk_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/xpc/EgVo2aDSNQ%3D%3D/cps/198/met/1777576737,/mh/Vu/mm/31,26/mn/sn-uxaxjvh5gbxoupo5-x1xll,sn-nja7snll/ms/au,onr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au,au/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/playlist_type/DVR/dover/13/txp/6218224/mt/1777576422/fvip/5/short_key/1/keepalive/yes/fexp/51565115,51565682/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,sgovp,rqh,xpc,bui,spc,vprv,playlist_type/sig/AHEqNM4wRgIhAMDxSizH1RGLD3ZFt9VXBT0khEqfeP_AaQu11jW99a3QAiEAyts7RdsOqKEYY0gJ7uOUPSMtMmPuOVxRce0_1qa2INU%3D/lsparams/hls_chunk_host,cps,met,mh,mm,mn,ms,mv,mvi,pcm2cms,pl,rms,initcwndbps/lsig/APaTxxMwRgIhAK-BbmiZhLDclubclVB9Uadve96SEc6eisWUqOeZPPnAAiEArAKamWdvI3sIfjEUmICGB-H614nJTUHEg-nUG3EkE58%3D/playlist/index.m3u8", "manifest_url": "https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/source/youtube/requiressl/yes/xpc/EgVo2aDSNQ%3D%3D/playback_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/cps/198/met/1777576737%2C/mh/Vu/mm/31%2C26/mn/sn-uxaxjvh5gbxoupo5-x1xll%2Csn-nja7snll/ms/au%2Conr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au%2Cau/tx/51773318/txs/51773317%2C51773318/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/go/1/rqh/5/mt/1777576422/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/51565115%2C51565682/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Cxpc%2Ctx%2Ctxs%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cbui%2Cspc%2Cvprv%2Cgo%2Crqh%2Citag%2Cplaylist_type/sig/AHEqNM4wRAIgKUmkyhslyYkPR-KL1dL_DtdGyLwNqUAmWkAylFX_A8UCIGOJ53oQe7nXd65HNztTuPCMRNXAO9w_6bYjMZQdxkih/lsparams/playback_host%2Ccps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpcm2cms%2Cpl%2Crms%2Cinitcwndbps/lsig/APaTxxMwRQIhAOovFXbPNaBZju3WBZnb9webS_PtZIyIeNcD6cOPz4BeAiA8OhPY1AytplhSixn8eTJhyeycq0hLy9J3_pL_0pAgTg%3D%3D/file/index.m3u8", "tbr": 171.542, "ext": "mp4", "fps": 30.0, "protocol": "m3u8_native", "preference": null, "quality": 0, "has_drm": false, "width": 144, "height": 256, "vcodec": "avc1.4D400C", "acodec": "none", "dynamic_range": "SDR", "source_preference": -1, "video_ext": "mp4", "audio_ext": "none", "abr": 0, "vbr": 171.542, "resolution": "144x256", "aspect_ratio": 0.56, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "269 - 144x256"}, {"format_id": "603", "format_index": null, "url": "https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/itag/603/source/youtube/requiressl/yes/ratebypass/yes/pfa/1/wft/1/sgovp/clen%3D788719%3Bdur%3D80.566%3Bgir%3Dyes%3Bitag%3D278%3Blmt%3D1723765248771469/rqh/1/hls_chunk_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/xpc/EgVo2aDSNQ%3D%3D/cps/198/met/1777576737,/mh/Vu/mm/31,26/mn/sn-uxaxjvh5gbxoupo5-x1xll,sn-nja7snll/ms/au,onr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au,au/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/playlist_type/DVR/dover/13/txp/6219224/mt/1777576422/fvip/5/short_key/1/keepalive/yes/fexp/51565115,51565682/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,pfa,wft,sgovp,rqh,xpc,bui,spc,vprv,playlist_type/sig/AHEqNM4wRQIgHtd44KzM1oZBEZN52mVt6McMOo-VA9F-pTAt200HeQcCIQD33CB5AllA91GbZAx6kjyOP8dhuMjzke8OPrCHMNfDMw%3D%3D/lsparams/hls_chunk_host,cps,met,mh,mm,mn,ms,mv,mvi,pcm2cms,pl,rms,initcwndbps/lsig/APaTxxMwRgIhAKGQYTc1rVKI934-NnhSNzWY-WwNMEL4UcCW5eUiK1mIAiEAuG2oDBEEgG0e8fsUxxWx585Oa7o8Eowhq1wNwq0jBeE%3D/playlist/index.m3u8", "manifest_url": "https://manifest.googlevideo.com/api/manifest/hls_variant/expire/1777598337/ei/IavzaYPkOuCYobIPzp6DiAY/ip/181.86.27.27/id/4306e9de6b8cee00/source/youtube/requiressl/yes/xpc/EgVo2aDSNQ%3D%3D/playback_host/rr6---sn-uxaxjvh5gbxoupo5-x1xll.googlevideo.com/cps/198/met/1777576737%2C/mh/Vu/mm/31%2C26/mn/sn-uxaxjvh5gbxoupo5-x1xll%2Csn-nja7snll/ms/au%2Conr/mv/m/mvi/6/pcm2cms/yes/pl/22/rms/au%2Cau/tx/51773318/txs/51773317%2C51773318/hfr/1/demuxed/1/tts_caps/1/maudio/1/initcwndbps/2313750/bui/AbKmrwq4-ZwwAPh8iluGcxrf50HtnVlGQfOUm54MBH1EKB5jmKSfPNFL3HgDeRtHGfsJl7vuazDi02Ng/spc/96Xrv37-Tsfh00kNnuZ67OUN-90roBIJVZ_k6pmf_CuBsrVxIdj6ynY2lCnqhWEu/vprv/1/go/1/rqh/5/mt/1777576422/fvip/5/nvgoi/1/short_key/1/ncsapi/1/keepalive/yes/fexp/51565115%2C51565682/dover/13/itag/0/playlist_type/DVR/sparams/expire%2Cei%2Cip%2Cid%2Csource%2Crequiressl%2Cxpc%2Ctx%2Ctxs%2Chfr%2Cdemuxed%2Ctts_caps%2Cmaudio%2Cbui%2Cspc%2Cvprv%2Cgo%2Crqh%2Citag%2Cplaylist_type/sig/AHEqNM4wRAIgKUmkyhslyYkPR-KL1dL_DtdGyLwNqUAmWkAylFX_A8UCIGOJ53oQe7nXd65HNztTuPCMRNXAO9w_6bYjMZQdxkih/lsparams/playback_host%2Ccps%2Cmet%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpcm2cms%2Cpl%2Crms%2Cinitcwndbps/lsig/APaTxxMwRQIhAOovFXbPNaBZju3WBZnb9webS_PtZIyIeNcD6cOPz4BeAiA8OhPY1AytplhSixn8eTJhyeycq0hLy9J3_pL_0pAgTg%3D%3D/file/index.m3u8", "tbr": 150.178, "ext": "mp4", "fps": 30.0, "protocol": "m3u8_native", "preference": null, "quality": 0, "has_drm": false, "width": 144, "height": 256, "vcodec": "vp09.00.11.08", "acodec": "none", "dynamic_range": "SDR", "source_preference": -1, "video_ext": "mp4", "audio_ext": "none", "abr": 0, "vbr": 150.178, "resolution": "144x256", "aspect_ratio": 0.56, "http_headers": {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.17 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5", "Sec-Fetch-Mode": "navigate"}, "format": "603 - 144x256"}, {"format
WARNING: [youtube] Qwbp3muM7gA: Signature extraction failed: Some formats may be missing
WARNING: [youtube] Qwbp3muM7gA: Some web client https formats have been skipped as they are missing a url. YouTube is forcing SABR streaming for this client. See  https://github.com/yt-dlp/yt-dlp/issues/12482  for more details


**Considering documentation for setup**

I need to figure out the content for the video, but maybe the title would be enough. Using OCR on the storyboard could be overkill. I might check whether there's a public transcript, but that doesn't seem needed. The video mentions uploading videos to a vending machine, so downloading and sampling frames with ffmpeg might be a route. I should create a setup manual for the credentials, but I need to be cautious about storing sensitive information in the repo. I could note that they are test-only credentials and should be replaced on the production day.

**Updating documentation plans**

I need to update PLAN.md according to the repo rules. It might make sense to add a link to docs/index, but that depends on whether there are meaningful changes in the documentation. I'm considering whether to update next-steps, but if it’s solely about documentation, maybe it's unnecessary. The PLAN.md should definitely mention the manual addition and that the payment section has been reviewed. I'll also need to check the docs/index structure to correctly link the manual.

bash

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && sed -n '1,220p' docs/index.md

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && sed -n '1,140p' PLAN.md


# Docs Index

## How To Navigate
- `PLAN.md`: current repo execution state and next implementation steps
- `docs/next-steps-queue.md`: prioritized backlog with dependency states
- `docs/repo-map.md`: fast file-level entrypoint for each domain
- `docs/implemented-*.md`: shipped behavior only
- `docs/*-rfc.md`: proposed implementation slices
- `docs/*-plan.md`: route/product planning
- `docs/*-discovery*.md`: validation and research framing
- `docs/*-gap-analysis.md`: what Ballbox still needs to build
- `docs/maintainability-priorities-2026-03.md`: prioritized frontend/backend maintainability work for the next refactor cycle
- `docs/demo-iteration-mode.md`: rapid demo delivery model with anti-debt guardrails

## Operator Priority Path
1. `PLAN.md`
2. `docs/next-steps-queue.md`
3. `docs/repo-map.md`
4. `docs/demo-iteration-mode.md`
5. relevant `docs/implemented-*.md` for the touched domain

## Current Refactor Input
- `docs/maintainability-priorities-2026-03.md`

## Execution And Coordination
- `docs/next-steps-queue.md`
- `docs/repo-map.md`
- `docs/demo-iteration-mode.md`

## Implemented State
- `docs/implemented-host-app.md`
- `docs/implemented-network.md`
- `docs/implemented-payments.md`
- `docs/implemented-players.md`
- `docs/implemented-sports.md`
- `docs/implemented-tvs.md`

## Discovery And Strategy
- `docs/coaches-discovery-stage.md`
- `docs/coaches-form-current.md`
- `docs/atc-integration-discovery.md`
- `docs/coaches-opportunity-brief.md`
- `docs/coaches-research-repository.md`
- `docs/coaches-validation-checklist-2-weeks.md`
- `docs/coaches-solution-thesis.md`

## Current Build Docs
- `docs/classes-subroute-plan.md`
- `docs/classes-mvp-rfc.md`
- `docs/coaches-coordination-gap-analysis.md`
- `docs/atc-mock-integration-plan.md`
- `docs/payments-machine-rollout-rfc.md`
- `docs/payments-machine-implementation-rfc.md`
- `docs/tcn-payments-integration-plan.md`
- `docs/tcn-payments-open-questions.md`
- `docs/tcn-payments-execution-plan.md`
- `docs/payments-club-settlement-strategy.md`


# Ballbox Host App Plan

## Goal
- Convert `ballbox` into the host app for Ballbox platform.
- Preserve the current marketing home at `/`.
- Add a shared admin shell at `/admin`.
- Keep product boundaries explicit with dedicated subroutes.
- Converge on a shared `network` core model that all product domains reference.

## Current State
- Ballbox overall is still in validation stage for padel centers; there are no customers yet and near-term proof still depends on pilots with the first machines.
- `/` remains the marketing home.
- `/admin` is live as the shared Ballbox admin shell.
- Product-separated admin areas exist for `tvs`, `payments`, `network`, `sports`, and `players`.
- `/admin/screens` exists as the runtime entry area for TVs.
- `/player/[screenId]` is the lightweight TV runtime route served as plain HTML + JavaScript inside Ballbox.
- `/player/[screenId]` now exists as the lightweight TV runtime route served as plain HTML + JavaScript inside Ballbox.
- TVs are internal to Ballbox as a module, not a separate app.
- TVs admin and `/api/tvs/*` use an internal Ballbox ownership model for clubs, venues, screens, and campaigns.
- TVs runtime/admin already read Ballbox Postgres directly through Prisma.
- TVs runtime heartbeats and impressions already write to Ballbox DB-backed flows.
- TVs manifest/admin scheduling now respects campaign and assignment time windows.
- TVs now expose a strict Zod-backed API contract plus contract tests.
- TVs runtime now exposes on-screen diagnostics for cache/object-URL/manual parity verification on real devices.
- TVs runtime diagnostics now also support a one-click parity snapshot JSON export (playback/cache/network/reporting state + user agent + capture timestamp) to standardize real-device evidence capture.
- OPS-2 local parity rerun on 2026-04-19 identified two machine-local issues: stale TV seed scheduling and unstable repo Playwright `webServer` behavior on this Pi. TV seed scheduling is now fixed in `prisma/seed-data.ts`; remaining OPS-2 closure work is local parity rerun plus real-device parity capture.
- TVs player runtime cache/media/impression/heartbeat/manifest-refresh logic now lives in one shared core used by both the React player and the lightweight plain-JS player shell.
- Maintainability review for the next refactor cycle is documented in `docs/maintainability-priorities-2026-03.md`.
- Payments are now owned inside Ballbox host app: native MercadoPago webhook intake exists under `/notification/mercadopago` and resolves QR notifications by fetching the upstream order.
- Payments host app now also exposes native QR order creation under `/api/integrations/payments/orders`, generating canonical `externalReference` values required for approved-session persistence correlation.
- `/admin/payments` now also generates live MercadoPago QR orders for arbitrary amounts or per-product prices, with money inputs expressed in major units (for example `15.00` or `1000.00`), then shows the QR preview/order id for scan-and-pay validation.
- That admin QR surface is now positioned as a provider test/debug harness; production vending-machine payments should integrate through the TCN machine protocol and let the machine render the buyer-facing QR.
- Club revenue share is currently handled with scheduled manual settlements/bookkeeping; Mercado Pago native split stays deferred until the machine flow and club count justify it.
- Legacy approved-callback compatibility has been removed from operations; `/api/integrations/payments/approved` now returns `410` so only the native MercadoPago path remains active.
- `/admin/payments` now exposes Ballbox-local CRUD/reconciliation for approved payment sessions plus correlation checks against canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references.
- Payments admin session routes now read Prisma error helpers from `lib/prisma-errors.ts` instead of importing them via `lib/ballbox-store.ts`.
- Network core now has DB-backed admin CRUD for ATC-linked clubs, venues, and vending machines inside Ballbox.
- Prisma `Club.atcSportclubId` now matches live nullable data to prevent admin loader `P2032` crashes on legacy clubs without ATC linkage (`/admin/network`, `/admin/players`, `/admin/sports`, `/admin/tvs`).
- Clubs keep nullable `atcSportclubId` support for legacy/local rows, and repo now ships `db:backfill-atc-clubs` to reconcile local club names with ATC summary data when ids exist.
- Network club list/detail/admin query shaping has started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Network club create/update/delete helpers have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Network venue mutation/delete helpers have also started moving out of `lib/ballbox-store.ts` into dedicated backend modules.
- Network vending-machine route callers now import `lib/network-backend-vending-machines.ts` directly and read unique-constraint classification from `lib/prisma-errors.ts`.
- Network club/venue route callers and `/admin/network` page now import direct network backend modules (`lib/network-backend-clubs.ts`, `lib/network-backend-club-mutations.ts`, `lib/network-backend-venues.ts`) plus `lib/prisma-errors.ts`.
- Network route handlers now share `lib/http/network-route-helpers.ts` for JSON parsing plus success/error envelopes across club, venue, and vending-machine contract routes.
- Players, sports, and payments route handlers now also share `lib/http/*-route-helpers.ts` so public/admin contract surfaces no longer depend on deep route-local helper imports.
- Network admin frontend view extraction has started moving venue-level and club-level rendering blocks out of `components/network-admin-client.tsx` into smaller components.
- Network admin frontend section composition has now also started moving club-list rendering, create-club section wiring, and derived venue-form wiring out of `components/network-admin-client.tsx` so it trends further toward a shell.
- Network admin frontend client-state, mutation orchestration, and local form-state wiring have started moving out of `components/network-admin-client.tsx` into dedicated hooks so the client trends toward a shell.
- Network admin frontend form defaults and status-option constants now live in a shared client config so shell and child components consume one source of truth.
- Network admin frontend layout/class wiring now also lives in a dedicated shell component so `components/network-admin-client.tsx` is closer to trivial hook composition.
- Network admin frontend shell chrome styles now also live in a tiny shared helper so the shell stays closer to pure composition/wiring.
- Players admin frontend client-state, form defaults, shell composition, and presentational stats/create/list-card seams now live in dedicated frontend modules so the client follows the same maintainable pattern already used by network.
- Sports admin frontend client-state, form defaults, shell composition, and coach/event section rendering now live in dedicated frontend seams so `components/sports-admin-client.tsx` is closer to pure wiring.
- Final naming direction is `network` core with `Club`, `Venue`, `VendingMachine`, `Screen`, and `TvDevice`.
- Post-`pnpm db:push` plus `pnpm db:seed` verification confirms physical DB columns stay canonical as `vendingMachineId` and `tvDeviceId`, with no `machineId`, `deviceId`, `/devices`, or `PaymentSessionRef` persistence aliases reintroduced.
- TVs admin now has DB-backed CRUD for screens and tv devices inside Ballbox.
- TV device admin mutations have started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV screen admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV campaign admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV creative admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV assignment admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV admin venue-option query shaping has now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV admin device/screen/campaign operations query shaping has now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV runtime source/manifest/heartbeat/impression helpers now also delegate from `lib/ballbox-store.ts` into a dedicated TV runtime backend module.
- TVs admin now has DB-backed CRUD for campaigns, creatives, and screen assignments inside Ballbox.
- Admin now supports a simple shared-password access layer when `BALLBOX_ADMIN_PASSWORD` is configured.
- TVs impressions now reject creatives that are not active for the target screen.
- Players API now reads Ballbox Postgres for `Player` and `Membership` rollups.
- Player backend read/admin query+mutation helpers have now started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Sports coach backend helpers have now started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Sports events plus classes offer/request backend helpers have now also started moving out of `lib/ballbox-store.ts` into dedicated backend modules.
- Sports now has DB-backed entities and admin/read APIs for coaches, open matches, and classes.
- `/classes` now exists as the first non-admin public Ballbox surface.
- `/classes` now lists Ballbox-local `ClassOffer` records, shows offer detail, surfaces visible ATC public-read slots, and accepts local `ClassRequest` submissions with either a concrete selected slot or a flexible any-schedule/any-location fallback.
- `/classes-by-date` now ships as a zone-first reservation calendar driven by coach availability plus ATC club availability, including explicit “coach disponible, pero sin canchas” slots and a required first zone selection based on the club's public location label when available.
- `/classes/me` now ships as a lightweight read-only student follow-up view (email-filtered request history) so demos can show the request loop without claiming full reservation confirmation.
- `/classes` initial HTML now renders from Ballbox-local offer data first and resolves ATC public-read context asynchronously after paint, with a short shared server cache to avoid repeating the same ATC lookup per visitor.
- `/classes` SSR no longer waits on ATC sportclub-summary hydration just to print club names, and `/classes-by-date` now parallelizes club/date ATC availability lookups behind a short shared server cache so first-byte time stays materially lower under production-like loads.
- Rapid demo execution now follows `docs/demo-iteration-mode.md` (`L1 story` / `L2 contracted demo` / `L3 production candidate`) so pivots stay fast without leaking demo shortcuts into core modules.
- `/coaches` now acts as the Ballbox account hub where the first differentiation is `Coach` vs `Club`.
- `/coaches` now defaults to `Sign in`, and `Sign up` is the fallback path for new accounts.
- `/coaches/settings` now lands on a coach workspace split into `Calendar`, `Performance`, `Alumnos`, and `Horarios y Clases`: calendar shows the current week in an hour-by-hour matrix centered on the coach's real student classes, grouping simultaneous reservations into one class card, surfacing participant basics (name, sexo, edad, categoria), and marking paid vs pending reservations, and now starts with a large `Agregar clase` CTA that jumps into `Alumnos`; `Performance` shows per-club performance plus weekly earnings and student consistency/review rows, `Alumnos` keeps roster + taken-hour management with the student table visible by default and `Agregar clase` opening a wide inline composer instead of a right-side summary panel, and `Horarios y Clases` now auto-saves club selection, availability, packages, and Mercado Pago setup without explicit save buttons while no longer exposing the club's court-fee pricing to the coach; pending autosave writes are now also flushed if the user leaves the page before the debounce finishes.
- `/clubs` now exists as the club-only service home that opens right after club sign-in/sign-up and groups the operational surfaces into service cards, with `Entrenadores` as the active entry point into the existing coaches workspace and the remaining services marked as `Proximamente`.
- `/clubs/coaches` now lands on a club workspace with top-level `Calendar`, `Analytics`, `Sedes`, and `Profesores` sections: calendar shows the current week's coach availability and reservations with sede/coach/court filters plus in-place manual class loading, analytics shows professor/reservation money splits and review placeholders, `Sedes` groups venue creation plus venue quick settings, and `Profesores` groups invite links plus the club coach table.
- `/clubs/coaches` now also enforces the club session at the page boundary before rendering the workspace, preventing the `Entrenadores` service-card hop from dropping users back into the shared `/coaches` access hub after a valid club login.
- `/clubs/coaches` now lands on a club workspace with top-level `Calendar`, `Analytics`, `Sedes`, and `Profesores` sections: calendar now works one sede at a time, switches between `Disponibilidad` and `Reservas`, uses clickable tag filters for sede/day/club-owned coaches instead of dropdowns, assigns a persistent color to each coach-enabled court, and shows the selected day with all coach-enabled courts as columns so clubs can see coverage or reservation gaps per court without selecting a court filter; coaches assigned with `Asignar todas las canchas` are treated as flexible coverage and occupy one free court per half-hour, not every court at once; manual class loading and pending requests open from dedicated action buttons above the calendar, and manual class creation now requires selecting an active coach package so clubs cannot create classes for coaches without packages; analytics shows professor/reservation money splits and review placeholders, `Sedes` now starts from a large `Agregar sede` CTA, keeps venue creation behind that action, and lists every venue in an editable table with a deep edit action, and `Profesores` groups invite links plus the club coach table.
- The `/clubs/coaches` calendar cells now avoid repeating redundant court-color bars or per-slot coverage counts inside each block; court color remains visible in the column header and the empty/cubierto cell state carries the coverage signal.
- Club workspace clickable actions are now visually distinguished with stronger accent borders, CTA chips, and hover/focus states, while venue detail edits auto-save in place instead of requiring explicit `Guardar` buttons or reloading the user back to the top, and pending autosave writes now also flush on navigation/unmount so venue/price/schedule edits are not lost when switching screens quickly.
- Club roster reads now tolerate production databases that have not yet received the newest coach/student schema additions: missing `Coach.assignedCourtId`, `CoachStudentBooking`, player contact columns, or coach package tables degrade to empty/null data instead of turning `/api/clubs/coaches` into a 500.
- `/api/coaches/students` now lets a coach load existing Ballbox players into concrete occupied time slots, validating availability, venue fit, and overlap rules, and the coach workspace now surfaces those students in a dedicated `Alumnos` section with WhatsApp / Telegram contact fields.
- `/api/coaches/students` now also supports weekly recurring student bookings, and the coach workspace `Alumnos` section can create new students directly before assigning them to one-off or recurring classes.
- `/api/clubs/players` now lets a club create its own students and auto-membership them into that club before assigning them to a professor from the club workspace manual class flow.
- External-coach availability now excludes venue slots already absorbed by club-coach availability, so external coaches only see residual hours after club coaches take priority.
- `/clubs/venues/[venueId]` now exists as the dedicated venue detail view for configuring venue opening/closing time, coach hours, external-coach availability, reservation settings, courts, and the coaches assigned to that venue, including which fixed court each coach covers in that sede; the page is now structured as a visible step-by-step workspace with quick jump cards so coach hours and court selection are no longer hidden inside collapsed panels.
- `CoachAvailability` weekly ranges and `CoachClub` selections now model coach intent separately from ATC court availability, while coach-side editing is clamped to the hours enabled by the assigned venue or the selected external clubs.
- Clubs now carry optional ATC sportclub ids to query availability at the club level.
- Clubs now also carry an optional contact email used for demo-grade club access and roster management.
- Venues now also store one opening time, one closing time, weekly coach-availability ranges, an external-coach availability flag, coach-enabled courts per venue, a special reservation price for coach classes, a club-coach-only discount on that fee, and a configurable late-cancellation window in hours.
- Coaches now can also be assigned to one venue inside their club.
- Coaches now also persist a Mercado Pago placeholder handle plus reusable sales-package rows for pricing, duration, class format, and max-student setup.
- Coaches and clubs now also store per-account password hashes for sign-in.
- Clubs now also persist coach invite tokens for generic join links, while still tolerating older email-bound invites.
- Local development now has a first-class Docker Postgres path via `docker-compose.yml` plus `.env.local`, and the seed now provisions demo coach/club passwords for local sign-in.
- Auth/session routes now return explicit database-unavailable errors when Prisma cannot reach or borrow a DB connection, instead of surfacing those failures as generic bad-login states.
- `/admin` overview now reads lightweight aggregate counts instead of hydrating full product datasets during SSR.
- Coach experience discovery is still early: current research input is an online form, so Ballbox does not yet have enough real-world coach workflow evidence to lock the sports product thesis.
- ATC admin/write is still not integrated; only a small public-read availability boundary now exists for `/classes` offer context and must degrade cleanly when unavailable.
- Local quality gate now includes `pnpm lint`, `pnpm typecheck`, `pnpm test:contracts`, `pnpm test:e2e`, and `pnpm build`.
- On `ballbox-first`, repo Playwright now uses a stable production-style `webServer` path for e2e (`pnpm build:e2e && pnpm start:e2e` on port `3020`), replacing the unstable Next 16 dev-server setup previously used on this Pi.
- Vitest taxonomy now includes layered includes for `tests/contracts`, `tests/unit`, and `tests/integration`, with dedicated scripts for each layer.
- Isolated backend/helper tests for classes, network client helpers, players client helpers, sports backend modules, and TVs runtime invariants now live under `tests/unit`, while route/schema surfaces stay under `tests/contracts`.

## Route Map
- `/`: marketing home
- `/admin`: admin overview
- `/admin/tvs`: TV and screen operations
- `/admin/payments`: payment operations and reconciliation
- `/admin/network`: clubs, venues, vending machines
- `/admin/sports`: open matches, coaches, academy
- `/admin/players`: players y memberships
- `/admin/screens`: screen runtime admin surface hosted inside Ballbox
- `/classes`: public class discovery + request surface
- `/classes/[offerId]`: public offer detail + request form
- `/classes-by-date`: zone-first reservation discovery calendar
- `/coaches`: account hub for coach vs club
- `/classes/me`: read-only student request follow-up
- `/coaches/settings`: coach workspace with `Calendar` / `Performance` / `Alumnos` / `Horarios y Clases`
- `/clubs`: club service home
- `/clubs/coaches`: club workspace with `Calendar` / `Analytics` / `Sedes` / `Profesores`
- authenticated coach/club workspace routes now use a shared top navbar with section tabs and an avatar-triggered sign-out menu instead of top-corner text links or standalone explainer hero panels
- coach workspace now also includes student follow-up and manually occupied-hour management using existing `Player` records, including coach-editable WhatsApp and Telegram contact data
- `/clubs/venues/[venueId]`: dedicated venue setup and venue roster

## Backend Split
- Ballbox Next.js host:
  - app shell
  - admin UI
  - shared auth/RBAC foundation
  - low-complexity CRUD


**Planning manual adaptations**

I need to create a manual based on the report, but I want to adapt it. Maybe I should copy the concise sections and note the source path and credentials. Including a section on payment integration validity review might be necessary. Linking to the docs and the manual section could be helpful too. I might also use ffmpeg for keyframe extraction from the video, but it's all a bit tricky. The user mentioned that there's a lot of info, so I’ll aim to keep it concise!

**Exploring frame extraction**

I’m thinking it might be better to manually inspect the frames and extract one every 10 seconds, using OCR for text recognition. I want to see if I can make this easier with ffmpeg. Perhaps I'll start by downloading a low-resolution version of the video to sample frames. This could help streamline the process and ensure I get the information I need effectively! Let’s see how this all works out!

set -e
out=/tmp/yt-tcn-manual2
mkdir -p "$out"
yt-dlp -f '269/160/18' -o "$out/%(id)s.%(ext)s" 'https://www.youtube.com/watch?v=Qwbp3muM7gA' >/tmp/yt.log 2>&1 || true
ls -l "$out"
ffmpeg -i "$out"/* -vf fps=1/10 "$out/frame-%03d.jpg" -hide_banner -loglevel error
ls "$out"/frame-*.jpg | sed -n '1,20p'


total 1120
-rw-r--r-- 1 sebas sebas 1133458 Aug 15  2024 Qwbp3muM7gA.mp4
/tmp/yt-tcn-manual2/frame-001.jpg
/tmp/yt-tcn-manual2/frame-002.jpg
/tmp/yt-tcn-manual2/frame-003.jpg
/tmp/yt-tcn-manual2/frame-004.jpg
/tmp/yt-tcn-manual2/frame-005.jpg
/tmp/yt-tcn-manual2/frame-006.jpg
/tmp/yt-tcn-manual2/frame-007.jpg
/tmp/yt-tcn-manual2/frame-008.jpg


**Exploring OCR tools**

I’m thinking about using OCR, perhaps Tesseract? I could look into combining that with an image-reading tool. I wonder if using the read image function would be helpful, although it might not attach well. I think it’d make sense to stick with the Tesseract command since it seems more reliable. I’ll evaluate the best approach to get the images read effectively. Let's get started on this!

for f in /tmp/yt-tcn-manual2/frame-*.jpg; do echo '---' $f; tesseract "$f" stdout 2>/dev/null | sed -n '1,40p'; done


--- /tmp/yt-tcn-manual2/frame-001.jpg
--- /tmp/yt-tcn-manual2/frame-002.jpg
--- /tmp/yt-tcn-manual2/frame-003.jpg
--- /tmp/yt-tcn-manual2/frame-004.jpg
--- /tmp/yt-tcn-manual2/frame-005.jpg
--- /tmp/yt-tcn-manual2/frame-006.jpg
--- /tmp/yt-tcn-manual2/frame-007.jpg
--- /tmp/yt-tcn-manual2/frame-008.jpg


write

# Vending machine setup day manual

## Purpose
- One Ballbox-owned setup manual for arrival day.
- Focus: first power-on, cloud onboarding, media upload, payment readiness, and go-live checks.
- Target machine family from current research: TCN CSC-8C(V49) or closest 8C/49-inch equivalent delivered by supplier.

## Inputs saved for this manual
- Research base: `/Users/sebas/Downloads/deep-research-report.md`
- Video reference: `https://www.youtube.com/watch?v=Qwbp3muM7gA` (`How to upload videos to the vending machine`)
- Test cloud: `https://dnymer.ourvend.com/#/login`
- Test user: `cc`
- Test password: `ys123456`
- Production note: real cloud + production creds arrive on machine-delivery day.

## Non-negotiables
- Do not blind-flash firmware from another model family.
- Ask TCN/supplier for exact package matched to serial number, mainboard revision, screen family, and payment-module BOM.
- Prefer Ethernet first.
- Set correct date, time, timezone before any cloud or payment test.
- Do not enable live payments until cloud status, product sync, and vend tests are clean.

## Day-before checklist
- Have Chrome ready.
- Have Ethernet available on-site.
- Have extension cable / stable power / grounding verified.
- Have phone hotspot as fallback only.
- Have Ballbox test payment account ready.
- Prepare a shared note with:
  - machine serial
n  - model label
  - screen family
  - payment module model
  - cloud device id / qr / barcode if shown
  - venue / club / machine nickname
- Ask supplier in advance for:
  - tenant invite or confirmed cloud login
  - exact firmware/build package
  - service PIN/password
  - open-device / payment API docs
  - device ids / serial mapping

## Arrival and receiving
1. Photograph crate before opening.
2. Record damage, pallet status, labels, serial stickers.
3. Confirm actual machine label.
4. Remove all transport foam, tape, tray locks, pickup-box fillers.
5. Check:
   - screen glass
   - bezel alignment
   - lock / door
   - pickup box flap
   - payment bay
   - leveling feet
6. Level machine before vend tests.
7. Record all ids in shared note.

## First power-on
1. Verify local voltage and grounding.
2. Power on with Ethernet connected if possible.
3. Set:
   - date
   - time
   - timezone
   - language / locale
4. Confirm internet reachability.
5. Test touch on corners, lane buttons, payment buttons.
6. If touch drifts:
   - clean glass
   - remove temporary protector
   - retry
   - use built-in diagnostics if present
   - escalate to TCN for exact touch/display package if still broken

## Cloud onboarding in OurVend
Portal:
- URL: `https://dnymer.ourvend.com/#/login`
- Browser: Chrome preferred
- Current test creds:
  - user: `cc`
  - pass: `ys123456`

Suggested order:
1. Login.
2. Confirm tenant, timezone, currency, and role permissions.
3. Create or confirm device-group hierarchy:
   - Country > Region > Club > Machine
4. Create/bind machine record with:
   - serial number
   - model family
   - location
   - timezone
   - language
   - temperature profile
   - currency
5. Confirm machine appears online.
6. Confirm heartbeat / last-seen timestamps move.
7. Load initial catalog.
8. Push one safe remote change:
   - product name or price
9. Confirm device receives sync.

## Media upload to the vending machine
Source: public video `How to upload videos to the vending machine`.

Use this as the operator checklist once inside tenant UI:
1. Find device-management / media / advertising section.
2. Upload one short test video first.
3. Assign video to the correct machine or machine group.
4. Publish/sync.
5. Confirm playback on the machine screen.
6. Then upload final production media pack.

Day-of validation points:
- supported format accepted by portal
- upload completes without silent failure
- media can be assigned to the specific machine
- machine pulls new media after sync/reboot
- playback is full-screen and touch flow still works

Open items to confirm in tenant UI:
- exact menu path for media upload
- supported file types / size limits
- whether media binds per machine, per group, or per playlist
- whether manual sync/reboot is needed after publish

## Payment integration status
### Current Ballbox position
- `/admin/payments` stays a Ballbox operator test/debug harness.
- Production vending flow should go through TCN machine protocol.
- The machine should show the buyer-facing QR on its own screen.
- Ballbox should own payment creation, webhook validation, approval persistence, and reconciliation.

### Why this still looks valid
- Current research still supports OurVend/TCN as cloud + device-control layer, not the owner of Ballbox business logic.
- Public evidence still points to secret/signature-based device-open access on the vendor side.
- The deep research report still supports backend-owned dynamic QR plus vendor-side vend authorization as the safest default architecture before exact tenant docs arrive.
- Nothing in the supplied test cloud login changes that direction by itself.

### What still needs real-world confirmation on setup day
- exact payment-init payload from machine/cloud
- exact vend-success callback / notify path
- exact `CodeUrl` expectation: qr payload vs link vs provider-specific deep link
- whether payment completion relies on callback, polling, or both
- exact auth/signature shape for production tenant endpoints

### Setup-day payment test sequence
1. Confirm machine online in cloud.
2. Confirm one vendable product exists and is mapped correctly.
3. Confirm Ballbox test payment flow can still create QR orders.
4. Confirm machine/cloud can request payment init against Ballbox adapter or stub.
5. Scan QR from phone.
6. Confirm Ballbox receives provider webhook.
7. Confirm approved payment persists with canonical machine correlation.
8. Confirm TCN side receives success/notify signal.
9. Confirm physical vend succeeds.
10. Reconcile payment, vend result, and inventory change.

## Go-live acceptance
- machine online and stable
- correct timezone
- touch OK across screen
- at least one product sync OK
- price sync OK
- media sync OK
- payment test OK
- vend test OK
- retry/error path observed at least once
- all ids and creds archived in internal ops note

## Escalation packet for supplier / TCN
If something blocks setup, send all of this together:
- machine serial
- model label
- screen family
- payment module model
- current firmware/build version
- cloud tenant name/url
- screenshots/video of issue
- timestamps with timezone
- whether Ethernet or Wi-Fi
- last successful action

## Related repo docs
- `docs/tcn-payments-integration-plan.md`
- `docs/tcn-payments-open-questions.md`
- `docs/tcn-payments-execution-plan.md`
- `docs/payments-machine-rollout-rfc.md`


Successfully wrote 6744 bytes to /mnt/rpi/worktrees/ballbox/docs-vending-manual/docs/vending-machine-setup-day-manual.md


**Fixing typos and updating index**

I noticed a typo in the line that needs correction. I should update the index accordingly to make sure everything is accurate. This is part of my plan to keep things relevant and organized. Let’s edit that typo and ensure the information is clear and up-to-date! It’s all about making those little adjustments for the best final result. Okay, time for a little text fix!

edit

- Prepare a shared note with:
  - machine serial
n  - model label

- Prepare a shared note with:
  - machine serial
  - model label

## Media upload to the vending machine
Source: public video `How to upload videos to the vending machine`.
- Treat this section as an operator checklist, not as a verified exact click-path yet.


Successfully replaced 2 block(s) in /mnt/rpi/worktrees/ballbox/docs-vending-manual/docs/vending-machine-setup-day-manual.md.

     ...
  27 - Have phone hotspot as fallback only.
  28 - Have Ballbox test payment account ready.
  29 - Prepare a shared note with:
  30   - machine serial
- 31 n  - model label
+ 31   - model label
  32   - screen family
  33   - payment module model
  34   - cloud device id / qr / barcode if shown
  35   - venue / club / machine nickname
     ...
 101 9. Confirm device receives sync.
 102
 103 ## Media upload to the vending machine
 104 Source: public video `How to upload videos to the vending machine`.
+105 - Treat this section as an operator checklist, not as a verified exact click-path yet.
 105
 106 Use this as the operator checklist once inside tenant UI:
 107 1. Find device-management / media / advertising section.
 108 2. Upload one short test video first.
     ...


- `docs/demo-iteration-mode.md`: rapid demo delivery model with anti-debt guardrails
- `docs/vending-machine-setup-day-manual.md`: operator setup-day manual for first TCN vending-machine arrival, cloud onboarding, media upload, and payment readiness

## Setup And Operations
- `docs/vending-machine-setup-day-manual.md`

## Current Build Docs


Successfully replaced 2 block(s) in /mnt/rpi/worktrees/ballbox/docs-vending-manual/docs/index.md.

    ...
 10 - `docs/*-discovery*.md`: validation and research framing
 11 - `docs/*-gap-analysis.md`: what Ballbox still needs to build
 12 - `docs/maintainability-priorities-2026-03.md`: prioritized frontend/backend maintainability work for the next refactor cycle
 13 - `docs/demo-iteration-mode.md`: rapid demo delivery model with anti-debt guardrails
+14 - `docs/vending-machine-setup-day-manual.md`: operator setup-day manual for first TCN vending-machine arrival, cloud onboarding, media upload, and payment readiness
 14
 15 ## Operator Priority Path
 16 1. `PLAN.md`
 17 2. `docs/next-steps-queue.md`
    ...
 43 - `docs/coaches-research-repository.md`
 44 - `docs/coaches-validation-checklist-2-weeks.md`
 45 - `docs/coaches-solution-thesis.md`
 46
+48 ## Setup And Operations
+49 - `docs/vending-machine-setup-day-manual.md`
+50
 47 ## Current Build Docs
 48 - `docs/classes-subroute-plan.md`
 49 - `docs/classes-mvp-rfc.md`
 50 - `docs/coaches-coordination-gap-analysis.md`
    ...


- That admin QR surface is now positioned as a provider test/debug harness; production vending-machine payments should integrate through the TCN machine protocol and let the machine render the buyer-facing QR.
- Repo docs now include `docs/vending-machine-setup-day-manual.md`, a Ballbox-owned setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding, media-upload checks, and payment go-live readiness.


Successfully replaced 1 block(s) in /mnt/rpi/worktrees/ballbox/docs-vending-manual/PLAN.md.

     ...
  29 - Payments are now owned inside Ballbox host app: native MercadoPago webhook intake exists under `/notification/mercadopago` and resolves QR notifications by fetching the upstream order.
  30 - Payments host app now also exposes native QR order creation under `/api/integrations/payments/orders`, generating canonical `externalReference` values required for approved-session persistence correlation.
  31 - `/admin/payments` now also generates live MercadoPago QR orders for arbitrary amounts or per-product prices, with money inputs expressed in major units (for example `15.00` or `1000.00`), then shows the QR preview/order id for scan-and-pay validation.
  32 - That admin QR surface is now positioned as a provider test/debug harness; production vending-machine payments should integrate through the TCN machine protocol and let the machine render the buyer-facing QR.
+ 33 - Repo docs now include `docs/vending-machine-setup-day-manual.md`, a Ballbox-owned setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding, media-upload checks, and payment go-live readiness.
  33 - Club revenue share is currently handled with scheduled manual settlements/bookkeeping; Mercado Pago native split stays deferred until the machine flow and club count justify it.
  34 - Legacy approved-callback compatibility has been removed from operations; `/api/integrations/payments/approved` now returns `410` so only the native MercadoPago path remains active.
  35 - `/admin/payments` now exposes Ballbox-local CRUD/reconciliation for approved payment sessions plus correlation checks against canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references.
  36 - Payments admin session routes now read Prisma error helpers from `lib/prisma-errors.ts` instead of importing them via `lib/ballbox-store.ts`.
     ...


**Considering Git Diff**

I think I might need to look into using "git diff" to compare changes. It's a handy command for reviewing differences between files or versions in a Git repository. I'll explore how to use it effectively, checking out the syntax and options available. It could help clarify any modifications and ensure I’m on the right track. Let's see how I can integrate it into my workflow to improve my coding process!

bash

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && git status --short

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && sed -n '1,260p' docs/vending-machine-setup-day-manual.md

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && git diff -- docs/index.md PLAN.md docs/vending-machine-setup-day-manual.md | sed -n '1,260p'


 M PLAN.md
 M docs/index.md
?? docs/vending-machine-setup-day-manual.md


# Vending machine setup day manual

## Purpose
- One Ballbox-owned setup manual for arrival day.
- Focus: first power-on, cloud onboarding, media upload, payment readiness, and go-live checks.
- Target machine family from current research: TCN CSC-8C(V49) or closest 8C/49-inch equivalent delivered by supplier.

## Inputs saved for this manual
- Research base: `/Users/sebas/Downloads/deep-research-report.md`
- Video reference: `https://www.youtube.com/watch?v=Qwbp3muM7gA` (`How to upload videos to the vending machine`)
- Test cloud: `https://dnymer.ourvend.com/#/login`
- Test user: `cc`
- Test password: `ys123456`
- Production note: real cloud + production creds arrive on machine-delivery day.

## Non-negotiables
- Do not blind-flash firmware from another model family.
- Ask TCN/supplier for exact package matched to serial number, mainboard revision, screen family, and payment-module BOM.
- Prefer Ethernet first.
- Set correct date, time, timezone before any cloud or payment test.
- Do not enable live payments until cloud status, product sync, and vend tests are clean.

## Day-before checklist
- Have Chrome ready.
- Have Ethernet available on-site.
- Have extension cable / stable power / grounding verified.
- Have phone hotspot as fallback only.
- Have Ballbox test payment account ready.
- Prepare a shared note with:
  - machine serial
  - model label
  - screen family
  - payment module model
  - cloud device id / qr / barcode if shown
  - venue / club / machine nickname
- Ask supplier in advance for:
  - tenant invite or confirmed cloud login
  - exact firmware/build package
  - service PIN/password
  - open-device / payment API docs
  - device ids / serial mapping

## Arrival and receiving
1. Photograph crate before opening.
2. Record damage, pallet status, labels, serial stickers.
3. Confirm actual machine label.
4. Remove all transport foam, tape, tray locks, pickup-box fillers.
5. Check:
   - screen glass
   - bezel alignment
   - lock / door
   - pickup box flap
   - payment bay
   - leveling feet
6. Level machine before vend tests.
7. Record all ids in shared note.

## First power-on
1. Verify local voltage and grounding.
2. Power on with Ethernet connected if possible.
3. Set:
   - date
   - time
   - timezone
   - language / locale
4. Confirm internet reachability.
5. Test touch on corners, lane buttons, payment buttons.
6. If touch drifts:
   - clean glass
   - remove temporary protector
   - retry
   - use built-in diagnostics if present
   - escalate to TCN for exact touch/display package if still broken

## Cloud onboarding in OurVend
Portal:
- URL: `https://dnymer.ourvend.com/#/login`
- Browser: Chrome preferred
- Current test creds:
  - user: `cc`
  - pass: `ys123456`

Suggested order:
1. Login.
2. Confirm tenant, timezone, currency, and role permissions.
3. Create or confirm device-group hierarchy:
   - Country > Region > Club > Machine
4. Create/bind machine record with:
   - serial number
   - model family
   - location
   - timezone
   - language
   - temperature profile
   - currency
5. Confirm machine appears online.
6. Confirm heartbeat / last-seen timestamps move.
7. Load initial catalog.
8. Push one safe remote change:
   - product name or price
9. Confirm device receives sync.

## Media upload to the vending machine
Source: public video `How to upload videos to the vending machine`.
- Treat this section as an operator checklist, not as a verified exact click-path yet.

Use this as the operator checklist once inside tenant UI:
1. Find device-management / media / advertising section.
2. Upload one short test video first.
3. Assign video to the correct machine or machine group.
4. Publish/sync.
5. Confirm playback on the machine screen.
6. Then upload final production media pack.

Day-of validation points:
- supported format accepted by portal
- upload completes without silent failure
- media can be assigned to the specific machine
- machine pulls new media after sync/reboot
- playback is full-screen and touch flow still works

Open items to confirm in tenant UI:
- exact menu path for media upload
- supported file types / size limits
- whether media binds per machine, per group, or per playlist
- whether manual sync/reboot is needed after publish

## Payment integration status
### Current Ballbox position
- `/admin/payments` stays a Ballbox operator test/debug harness.
- Production vending flow should go through TCN machine protocol.
- The machine should show the buyer-facing QR on its own screen.
- Ballbox should own payment creation, webhook validation, approval persistence, and reconciliation.

### Why this still looks valid
- Current research still supports OurVend/TCN as cloud + device-control layer, not the owner of Ballbox business logic.
- Public evidence still points to secret/signature-based device-open access on the vendor side.
- The deep research report still supports backend-owned dynamic QR plus vendor-side vend authorization as the safest default architecture before exact tenant docs arrive.
- Nothing in the supplied test cloud login changes that direction by itself.

### What still needs real-world confirmation on setup day
- exact payment-init payload from machine/cloud
- exact vend-success callback / notify path
- exact `CodeUrl` expectation: qr payload vs link vs provider-specific deep link
- whether payment completion relies on callback, polling, or both
- exact auth/signature shape for production tenant endpoints

### Setup-day payment test sequence
1. Confirm machine online in cloud.
2. Confirm one vendable product exists and is mapped correctly.
3. Confirm Ballbox test payment flow can still create QR orders.
4. Confirm machine/cloud can request payment init against Ballbox adapter or stub.
5. Scan QR from phone.
6. Confirm Ballbox receives provider webhook.
7. Confirm approved payment persists with canonical machine correlation.
8. Confirm TCN side receives success/notify signal.
9. Confirm physical vend succeeds.
10. Reconcile payment, vend result, and inventory change.

## Go-live acceptance
- machine online and stable
- correct timezone
- touch OK across screen
- at least one product sync OK
- price sync OK
- media sync OK
- payment test OK
- vend test OK
- retry/error path observed at least once
- all ids and creds archived in internal ops note

## Escalation packet for supplier / TCN
If something blocks setup, send all of this together:
- machine serial
- model label
- screen family
- payment module model
- current firmware/build version
- cloud tenant name/url
- screenshots/video of issue
- timestamps with timezone
- whether Ethernet or Wi-Fi
- last successful action

## Related repo docs
- `docs/tcn-payments-integration-plan.md`
- `docs/tcn-payments-open-questions.md`
- `docs/tcn-payments-execution-plan.md`
- `docs/payments-machine-rollout-rfc.md`


diff --git a/PLAN.md b/PLAN.md
index b147902..78aef33 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -30,6 +30,7 @@
 - Payments host app now also exposes native QR order creation under `/api/integrations/payments/orders`, generating canonical `externalReference` values required for approved-session persistence correlation.
 - `/admin/payments` now also generates live MercadoPago QR orders for arbitrary amounts or per-product prices, with money inputs expressed in major units (for example `15.00` or `1000.00`), then shows the QR preview/order id for scan-and-pay validation.
 - That admin QR surface is now positioned as a provider test/debug harness; production vending-machine payments should integrate through the TCN machine protocol and let the machine render the buyer-facing QR.
+- Repo docs now include `docs/vending-machine-setup-day-manual.md`, a Ballbox-owned setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding, media-upload checks, and payment go-live readiness.
 - Club revenue share is currently handled with scheduled manual settlements/bookkeeping; Mercado Pago native split stays deferred until the machine flow and club count justify it.
 - Legacy approved-callback compatibility has been removed from operations; `/api/integrations/payments/approved` now returns `410` so only the native MercadoPago path remains active.
 - `/admin/payments` now exposes Ballbox-local CRUD/reconciliation for approved payment sessions plus correlation checks against canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references.
diff --git a/docs/index.md b/docs/index.md
index 62ddcac..0a36683 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -11,6 +11,7 @@
 - `docs/*-gap-analysis.md`: what Ballbox still needs to build
 - `docs/maintainability-priorities-2026-03.md`: prioritized frontend/backend maintainability work for the next refactor cycle
 - `docs/demo-iteration-mode.md`: rapid demo delivery model with anti-debt guardrails
+- `docs/vending-machine-setup-day-manual.md`: operator setup-day manual for first TCN vending-machine arrival, cloud onboarding, media upload, and payment readiness

 ## Operator Priority Path
 1. `PLAN.md`
@@ -44,6 +45,9 @@
 - `docs/coaches-validation-checklist-2-weeks.md`
 - `docs/coaches-solution-thesis.md`

+## Setup And Operations
+- `docs/vending-machine-setup-day-manual.md`
+
 ## Current Build Docs
 - `docs/classes-subroute-plan.md`
 - `docs/classes-mvp-rfc.md`


**Thinking about commits**

I need to consider making a git commit. The repository advises committing freely as long as it makes sense, which is really helpful! Since I'm on a branch, I'll ensure I'm doing it correctly. It’s nice to have that level of flexibility. I'll just make sure that my commits are coherent so that I keep the project organized. Got to stay on top of keeping everything tidy in the repo!

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && git add PLAN.md docs/index.md docs/vending-machine-setup-day-manual.md && git commit -m "docs: add vending machine setup day manual"


[docs-vending-manual 3239857] docs: add vending machine setup day manual
 3 files changed, 194 insertions(+)
 create mode 100644 docs/vending-machine-setup-day-manual.md


**Considering memory-saving options**

I think I need to save some memory for a high-signal project document addition. Maybe I can use the memory-write skill, but I wonder if it's easier to use the API directly. I feel like I should check the skill instructions first, but it's likely okay if I don't. The developer mentioned creating memory when it's useful, and this feels useful to me. Should I load the memory-write skill? Let's read!


---
name: memory-write
description: Save a durable high-signal memory into agents-database
---

# memory-write

## Use When

- new information is likely useful beyond current chat or task
- saving a decision, preference, project fact, reusable source/artifact, or handoff summary
- user asks to remember something durable

## Do Not Use When

- content is trivial, redundant, ephemeral, or likely useless later
- content is secret unless user explicitly asked to store it

## Required Workflow

1. If useful context may already exist, optionally check first:
   - `curl -fsSL "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/search?q=<query>&scope=global&scope=project&limit=10"`
2. Choose best memory type:
   - `profile`: stable user preference, priority, recurring constraint
   - `decision`: chosen approach or operating rule
   - `project`: durable project fact or invariant
   - `artifact`: reusable source, claim, reference, output
   - `episode`: concise session summary, blocked state, handoff context
3. Build a concise payload with:
   - required: `type`, `scope`, `title`, `content`
   - recommended when known: `subtype`, `project_id`, `repo_id`, `task_id`, `run_id`, `source_ref`, `origin_agent`, `url`, `domain`, `metadata`
4. Save it with the helper:
   - `~/.agents/skills/memory-write/memory-write --type <type> --scope <scope> --title "<title>" --content "<content>"`
5. Confirm the created memory id and what was stored.

## Quick Examples

```bash
~/.agents/skills/memory-write/memory-write \
  --type profile \
  --subtype working_preference \
  --scope global \
  --title "User prefers concise status updates" \
  --content "Sebas prefers short, direct updates with business impact first." \
  --source-ref "conversation:2026-04-17" \
  --origin-agent personal-agent

~/.agents/skills/memory-write/memory-write \
  --type decision \
  --subtype operating_rule \
  --scope global \
  --title "Agents should save durable high-signal memory opportunistically" \
  --content "Treat agents-database as shared durable memory, not only search." \
  --metadata '{"source":"conversation"}'
```

## Rules

- prefer one strong memory over many weak ones
- keep `title` short and searchable
- keep `content` factual and compact
- use `episode` for summaries, not for every chat turn
- include `url` and `domain` for web-derived artifacts when known
- include `project_id`, `repo_id`, `task_id`, and `run_id` when context exists


**Storing project details**

I need to record an artifact or project, possibly titled "Added setup manual and payment validity review." I'll utilize a memory-write helper through Bash to accomplish this. It’s important to keep track of these details, as it helps with organization and referencing later on. I'll create a quick note to ensure that the information is saved properly so I can access it whenever needed.

~/.agents/skills/memory-write/memory-write --type artifact --scope repo --title "Vending setup day manual added" --content "Repo now has docs/vending-machine-setup-day-manual.md: a setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding with test portal creds, media-upload operator checks, and a payment-integration validity review that keeps Ballbox-owned dynamic QR + webhook logic and TCN-side QR display/vend authorization as the current direction." --subtype ops_runbook --project-id proj_ballbox --repo-id repo_ballbox --source-ref docs/vending-machine-setup-day-manual.md --origin-agent personal-agent --metadata '{"repo":"ballbox","branch":"docs-vending-manual","commit":"3239857"}'


Traceback (most recent call last):
  File "/home/sebas/.agents/skills/memory-write/memory-write", line 86, in <module>
    main()
    ~~~~^^
  File "/home/sebas/.agents/skills/memory-write/memory-write", line 80, in main
    with urllib.request.urlopen(req, timeout=30) as r:
         ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 189, in urlopen
    return opener.open(url, data, timeout)
           ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 495, in open
    response = meth(req, response)
  File "/usr/lib/python3.13/urllib/request.py", line 604, in http_response
    response = self.parent.error(
        'http', request, response, code, msg, hdrs)
  File "/usr/lib/python3.13/urllib/request.py", line 533, in error
    return self._call_chain(*args)
           ~~~~~~~~~~~~~~~~^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 466, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.13/urllib/request.py", line 613, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 500: FOREIGN KEY constraint failed

Command exited with code 1


~/.agents/skills/memory-write/memory-write --type artifact --scope repo --title "Vending setup day manual added" --content "Repo now has docs/vending-machine-setup-day-manual.md: a setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding with test portal creds, media-upload operator checks, and a payment-integration validity review that keeps Ballbox-owned dynamic QR + webhook logic and TCN-side QR display/vend authorization as the current direction." --subtype ops_runbook --source-ref docs/vending-machine-setup-day-manual.md --origin-agent personal-agent --metadata '{"repo":"ballbox","branch":"docs-vending-manual","commit":"3239857"}'


Vending setup day manual added

Repo now has docs/vending-machine-setup-day-manual.md: a setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding with test portal creds, media-upload operator checks, and a payment-integration validity review that keeps Ballbox-owned dynamic QR + webhook logic and TCN-side QR display/vend authorization as the current direction.

Summary: Repo now has docs/vending-machine-setup-day-manual.md: a setup-day runbook for first TCN machine arrival covering receiving, first power-on, OurVend tenant onboarding with test po…


Updated #1 (pending → completed)

Build vending machine setup manual from provided sources in isolated worktree

Create/update docs in a dedicated worktree using the provided links and local research file, then review whether the Payments Integration section still looks valid.


**Summarizing development notes**

I need to provide a final concise update including the worktree path, branch, and commit. It’s important to note that the payments integration is still valid, but there are caveats. Additionally, I'm aware that it's not on the main branch yet due to the lack of direct confirmation for the main merge. I might mention the option to cherry-pick or merge later since the user indicated to save this, but we need explicit confirmation for the main merge.

Hecho en worktree.

- worktree: `/mnt/rpi/worktrees/ballbox/docs-vending-manual`
- branch: `docs-vending-manual`
- commit: `3239857` — `docs: add vending machine setup day manual`

Cambios:
- `docs/vending-machine-setup-day-manual.md`
- `docs/index.md`
- `PLAN.md`

Qué guardé:
- manual de setup day
- cloud test + creds
- checklist de recepción, power-on, onboarding en OurVend, media upload, payment readiness
- revisión de vigencia de Payments Integration

Review de Payments Integration:
- Sí, sigue válida.
- Se mantiene:
  - `/admin/payments` = test/debug harness
  - flujo productivo = TCN protocol
  - QR buyer-facing = lo muestra la máquina
  - Ballbox = crea pago, valida webhook, persiste aprobación, reconcilia
- Lo no cerrado aún:
  - payload exacto de init
  - callback/notify exacto de vend success
  - qué espera `CodeUrl`
  - si hay polling además de callback
  - auth/signature real del tenant productivo

Límite:
- no lo metí en `main` porque merge/push a `main` requiere confirmación explícita.

Si querés, siguiente paso:
1. te pego resumen del manual acá, o
2. lo llevo a `main` localmente/armo PR, según me digas.


Fijate de dejar todos los documentos, porque ya teníamos un manual del día de setup. Dos creo, uno para mí que voy a estar remoto y otro para el que va a estar físico. Pero mirá la documentación en general.


**Inspecting documentation**

I need to inspect the existing manuals, which are likely located in the main or worktree directories. It seems like I need to update a new document and possibly merge or align it with what’s already there. I think focusing on clarity and consistency will be important during this task. Let’s make sure everything is in order before proceeding, so it’s smooth sailing for anyone who accesses the documents later!

todo

Audit existing setup-day docs and align vending manuals

Review repo documentation for existing setup-day manuals, especially remote vs on-site roles, then consolidate/index/update docs so all relevant manuals remain visible and non-duplicative.


Created #2: Audit existing setup-day docs and align vending manuals (pending)


bash

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && rg -n "setup day|setup-day|manual|runbook|on-site|onsite|remote|vending|ourvend|TCN|arrival" docs PLAN.md README.md | sed -n '1,260p'

cd /mnt/rpi/worktrees/ballbox/docs-vending-manual && find docs -maxdepth 1 -type f | sort | sed -n '1,240p'