{"result":{"depiction_url":null,"edges":{"in_module":{"objects":[{"created":"2026-09-29T13:07:42Z","object_id":{"id":1296,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_base","title":"mod_base","uri":"https:\/\/zotonic.com\/id\/1296"},"seq":1}],"predicate":{"id":333,"is_a":["meta","predicate"],"name":"in_module","title":{"_type":"trans","tr":{"en":"In module"}},"uri":"https:\/\/zotonic.com\/id\/in_module"}},"subject":{"objects":[{"created":"2026-09-29T13:31:41Z","object_id":{"id":2555,"is_a":["categorization","keyword","keyword_information_type"],"name":"zotonic_topic_reference","title":"Reference","uri":"https:\/\/zotonic.com\/id\/2555"},"seq":1},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2565,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_integrator","title":"Integrator","uri":"https:\/\/zotonic.com\/id\/2565"},"seq":2},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2576,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_media_management","title":"Media management","uri":"https:\/\/zotonic.com\/id\/2576"},"seq":3},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2590,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_authentication","title":"Authentication","uri":"https:\/\/zotonic.com\/id\/2590"},"seq":4},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2599,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_api_and_integration","title":"API and integration","uri":"https:\/\/zotonic.com\/id\/2599"},"seq":5},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2606,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_scheduled_and_background_work","title":"Scheduled and background work","uri":"https:\/\/zotonic.com\/id\/2606"},"seq":6},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2626,"is_a":["categorization","keyword","keyword_architecture"],"name":"zotonic_topic_controller","title":"Controller","uri":"https:\/\/zotonic.com\/id\/2626"},"seq":7},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2674,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_json","title":"JSON","uri":"https:\/\/zotonic.com\/id\/2674"},"seq":8},{"created":"2026-09-29T13:31:41Z","object_id":{"id":2676,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_http","title":"HTTP","uri":"https:\/\/zotonic.com\/id\/2676"},"seq":9}],"predicate":{"id":308,"is_a":["meta","predicate"],"name":"subject","title":{"_type":"trans","tr":{"en":"Keyword"}},"uri":"http:\/\/purl.org\/dc\/elements\/1.1\/subject"}}},"id":2707,"is_a":["text","documentation","reference","controller"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/2707"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_controller_controller_media_runner_callback","page_url":{"en":"https:\/\/zotonic.com\/docs\/2707\/controller_media_runner_callback","x-default":"https:\/\/zotonic.com\/docs\/2707\/controller_media_runner_callback"},"preview_url":null,"resource":{"version":23,"pivot_location_lat":null,"title":"controller_media_runner_callback","is_authoritative":true,"body":"<p>Client-side receiver for results produced by the external\n<a href=\"https:\/\/github.com\/zotonic\/mediarunner\">media runner<\/a> site.\nThis controller belongs to <code>mod_base<\/code>, so each client site has a callback endpoint\nwithout installing the runner site itself.<\/p>\n<h2>Endpoint<\/h2>\n<p>The <code>media_runner_callback<\/code> dispatch accepts <code>POST \/media-runner\/callback?id=&lt;job-id&gt;<\/code>\nwith <code>Content-Type: application\/json<\/code> and <code>Authorization: Bearer &lt;secret&gt;<\/code>.\nThe body must be a JSON object containing the runner&#39;s result envelope. Its size\nis bounded by <code>z_media_runner_protocol:callback_limit\/0<\/code> before JSON decoding.<\/p>\n<h2>Place in the processing flow<\/h2>\n<ol><li>A client calls <code>z_exec:run\/4<\/code> with its site context. When remote processing is\nconfigured, <code>z_media_runner<\/code> registers the waiting process, creates a job ID and\nrandom callback secret, and builds an absolute URL using the client&#39;s\n<code>media_runner_callback<\/code> dispatch rule.<\/li><li>The client submits the job to the runner using OAuth2. The job includes that\ncallback URL and secret. The runner queues the command, executes it in the\nsandbox, and POSTs its JSON result here with <code>?id=&lt;job-id&gt;<\/code> and the secret in\nthe <code>Authorization: Bearer &lt;secret&gt;<\/code> header.<\/li><li>This controller authenticates the callback and forwards the decoded result to\n<code>z_media_runner<\/code>. The waiting client process then uses\n<code>z_media_runner_protocol:unpack\/2<\/code> to validate the result and restore output\nfiles to the paths declared by the original caller. This controller does not\nexecute commands or write the returned media files.<\/li><\/ol>\n<h2>Callback authentication<\/h2>\n<p>The dispatch rule is anonymous deliberately: this endpoint authenticates with the\nper-job callback secret, not the OAuth2 token used to submit jobs to the runner,\nnor a browser session. <code>z_media_runner<\/code> keeps only the secret&#39;s hash and associates\nit with the waiting process. Both the job ID and secret must match.<\/p>\n<p>The request query is already parsed before <code>is_authorized\/1<\/code>. Reading the job ID\nthere lets us reject unauthorized requests before allocating the bounded result\nbody. Responses have cache prevention headers; media payloads and callback secrets\nare not logged.<\/p>\n<h2>Delivery and lifetime<\/h2>\n<p>HTTP 204 acknowledges delivery to the waiting process, including a result that\nreports a processing failure. It does not mean that output validation succeeded.\nMalformed JSON or a non-map result returns 400; missing or malformed credentials\nreturn 401. Unknown jobs and non-matching secrets return 410. The runner treats\n410 as final and stops retrying that callback.<\/p>\n<p>While the job is registered, duplicate authenticated callbacks are acknowledged\nwithout notifying the waiting process again. The registration is removed when the\ncall finishes, times out, or its process dies. Late callbacks then return 410.\nRegistrations are in memory on the submitting Zotonic node; cluster routing must\nsend callbacks back to that node, and a node restart loses pending registrations.<\/p>\n<p>See the <a href=\"https:\/\/github.com\/zotonic\/mediarunner#readme\">media runner documentation<\/a>\nfor deployment and client configuration.<\/p>","slug":"controller_media_runner_callback","is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"d800c8bb9488c57736d0b196d36e000ec8dec89faa3c69a49abab4953aef0bee","is_featured":false,"content_group_id":{"id":2551,"is_a":["meta","content_group"],"name":"content_group_imported_docs","title":"Imported documentation","uri":"https:\/\/zotonic.com\/id\/content_group_imported_docs"},"category_id":{"id":321,"is_a":["meta","category"],"name":"controller","title":"Controllers","uri":"https:\/\/test.zotonic.com\/id\/321"},"doc_source_path":"apps\/zotonic_mod_base\/src\/controllers\/controller_media_runner_callback.erl","publication_start":"2026-09-29T13:07:42Z","github_url":"https:\/\/github.com\/zotonic\/zotonic\/blob\/master\/apps\/zotonic_mod_base\/src\/controllers\/controller_media_runner_callback.erl","pivot_location_lng":null,"doc_source_kind":"controller","name":"doc_controller_controller_media_runner_callback","is_unfindable":false,"is_published":true,"pivot_geocode":null,"created":"2026-09-29T13:07:42Z","uri":null,"doc_status":"current","is_dependent":false,"erlang_app":"zotonic_mod_base","publication_end":"9999-06-01T00:00:00Z","modifier_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"privacy":0,"doc_source_commit":"85498256abd162b9bc082d43a90c995844fd4408\n","erlang_module":"controller_media_runner_callback","creator_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"modified":"2026-09-29T14:40:14Z","title_slug":"controller_media_runner_callback"},"uri":"https:\/\/zotonic.com\/id\/2707","uri_template":"https:\/\/zotonic.com\/id\/:id","websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/2707"}},"status":"ok"}