{"result":{"depiction_url":null,"edges":{"in_module":{"objects":[{"created":"2020-05-30T05:48:09Z","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":1000000}],"predicate":{"id":333,"is_a":["meta","predicate"],"name":"in_module","title":{"_type":"trans","tr":{"en":"In module"}},"uri":"https:\/\/zotonic.com\/id\/in_module"}},"relation":{"objects":[{"created":"2020-05-30T05:48:09Z","object_id":{"id":1541,"is_a":["text","documentation","developerguide"],"name":"doc_developerguide_dispatch_rules","title":{"_type":"trans","tr":{"en":"Follow a request to its template"}},"uri":"https:\/\/zotonic.com\/id\/1541"},"seq":1000000},{"created":"2020-05-30T05:48:09Z","object_id":{"id":1540,"is_a":["text","documentation","developerguide"],"name":"doc_developerguide_controllers","title":{"_type":"trans","tr":{"en":"Add an HTTP controller"}},"uri":"https:\/\/zotonic.com\/id\/1540"},"seq":1000000}],"predicate":{"id":303,"is_a":["meta","predicate"],"name":"relation","title":{"_type":"trans","tr":{"nl":"Relatie","en":"Relation"}},"uri":"http:\/\/purl.org\/dc\/terms\/relation"}},"subject":{"objects":[{"created":"2026-09-09T13:47:54Z","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-09T13:47:54Z","object_id":{"id":2564,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_backend_developer","title":"Backend developer","uri":"https:\/\/zotonic.com\/id\/2564"},"seq":2},{"created":"2026-09-09T13:47:54Z","object_id":{"id":2592,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_authorization_and_access_control","title":"Authorization and access control","uri":"https:\/\/zotonic.com\/id\/2592"},"seq":3},{"created":"2026-09-09T13:47:54Z","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":4},{"created":"2026-09-09T13:47:54Z","object_id":{"id":2626,"is_a":["categorization","keyword","keyword_architecture"],"name":"zotonic_topic_controller","title":"Controller","uri":"https:\/\/zotonic.com\/id\/2626"},"seq":5},{"created":"2026-09-09T13:47:54Z","object_id":{"id":2676,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_http","title":"HTTP","uri":"https:\/\/zotonic.com\/id\/2676"},"seq":6}],"predicate":{"id":308,"is_a":["meta","predicate"],"name":"subject","title":{"_type":"trans","tr":{"en":"Keyword"}},"uri":"http:\/\/purl.org\/dc\/elements\/1.1\/subject"}}},"export_revision":3514,"id":1875,"is_a":["text","documentation","reference","controller"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/1875"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_controller_controller_api","page_url":{"en":"https:\/\/zotonic.com\/docs\/1875\/controller_api","x-default":"https:\/\/zotonic.com\/docs\/1875\/controller_api"},"preview_url":null,"resource":{"version":3514,"pivot_location_lat":null,"title":"controller_api","is_authoritative":true,"body":"<p><code>controller_api<\/code> exposes MQTT topics over HTTP at <code>\/api\/:topic<\/code>, including model calls.\nRequests use the normal MQTT and model access checks for the current user.<\/p>\n<h2>Model calls<\/h2>\n<p>The topic can refer to a model. In this case the topic pattern is one of:<\/p>\n<ul><li><code>\/api\/model\/mymodel\/get\/foo\/bar<\/code> maps to <code>m_mymodel:m_get([ &lt;&lt;&quot;foo&quot;&gt;&gt;, &lt;&lt;&quot;bar&quot;&gt;&gt; ], Msg, Context)<\/code><\/li><li><code>\/api\/model\/mymodel\/post\/foo\/bar<\/code> maps to <code>m_mymodel:m_post([ &lt;&lt;&quot;foo&quot;&gt;&gt;, &lt;&lt;&quot;bar&quot;&gt;&gt; ], Msg, Context)<\/code><\/li><li><code>\/api\/model\/mymodel\/delete\/foo\/bar<\/code> maps to <code>m_mymodel:m_delete([ &lt;&lt;&quot;foo&quot;&gt;&gt;, &lt;&lt;&quot;bar&quot;&gt;&gt; ], Msg, Context)<\/code><\/li><\/ul>\n<p>In all cases, <code>Msg<\/code> is an MQTT message map, with the <code>payload<\/code> set to the body of the received request. In case of a\nGET, HEAD or DELETE the payload is set to a map of the query arguments.<\/p>\n<p>Note that for a POST the payload might not be the complete message as additional query arguments are passed via\n<code>z_context:get_q(&lt;&lt;&quot;argument_name&quot;&gt;&gt;, Context)<\/code>. If the model function is called using MQTT, then all arguments\nare contained in the payload.<\/p>\n<h2>HTTP methods<\/h2>\n<table class=\"table\"><thead><tr><th>HTTP method<\/th><th>Supported topics<\/th><\/tr><\/thead><tbody><tr><td>GET<\/td><td>Model <code>get<\/code> topics and non-model topics.<\/td><\/tr><tr><td>HEAD<\/td><td>Model <code>get<\/code> topics only.<\/td><\/tr><tr><td>POST<\/td><td>Any topic, including model <code>get<\/code>, <code>post<\/code> and <code>delete<\/code> topics.<\/td><\/tr><tr><td>DELETE<\/td><td>Model <code>delete<\/code> topics only.<\/td><\/tr><tr><td>OPTIONS<\/td><td>Any topic; does not call the model or publish a message.<\/td><\/tr><\/tbody><\/table>\n<p>A method\/topic mismatch returns <code>400 Bad Request<\/code>. Known HTTP methods outside this list return\n<code>405 Method Not Allowed<\/code>; unrecognized methods return <code>501 Not Implemented<\/code>.\nThe <code>Allow<\/code> header lists the controller&#39;s methods, rather than\nfiltering them for the requested topic.<\/p>\n<p>HEAD executes the model call and returns the same status and headers as GET, without a\nresponse body. It still performs authorization and model validation; it does not return\nsuccess without checking the result. For example, to check whether all sites are running:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-sh\">curl --head https:\/\/example.com\/api\/model\/zotonic_status\/get\/check\n<\/code><\/pre>\n<p>On a site exposing the <code>zotonic_status<\/code> model, this returns <code>200 OK<\/code> when all sites are running,\nor <code>500 Internal Server Error<\/code> when the check fails.<\/p>\n<h2>Replies<\/h2>\n<p>By default, the controller publishes a message to the topic and waits up to 60 seconds for\nits response. The response is encoded in the negotiated content type, with JSON as the default.\nModel errors determine the HTTP status, for example <code>403<\/code> for <code>eacces<\/code> and <code>404<\/code> for <code>enoent<\/code>\nor <code>unknown_path<\/code>.<\/p>\n<p>With the <code>response_topic<\/code> query argument, the controller publishes the message and returns\nwithout waiting for the model&#39;s result. A non-empty value supplies the MQTT response topic;\nan empty value publishes without a response topic. The HTTP reply then reports the outcome\nof publishing, not the outcome of the model call.<\/p>\n<p>HEAD rejects any <code>response_topic<\/code> query argument with <code>400 Bad Request<\/code>, including an empty\nvalue or a bare <code>?response_topic<\/code>. This ensures that HEAD waits for the model&#39;s result.<\/p>","slug":"controller_api","is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"f6558442e1bbc4abe8f341d10a27a0519c120d0edc24d0dbe928e82dcfa2ef29","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_api.erl","publication_start":"2023-01-30T19:24:02Z","github_url":"https:\/\/github.com\/zotonic\/zotonic\/blob\/master\/apps\/zotonic_mod_base\/src\/controllers\/controller_api.erl","pivot_location_lng":null,"doc_source_kind":"controller","name":"doc_controller_controller_api","is_unfindable":false,"is_published":true,"pivot_geocode":null,"created":"2020-05-30T05:47:41Z","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":"8e6aad17c4d86dee983db46a656ed6f6a930bcbb\n","erlang_module":"controller_api","creator_id":{"id":336,"is_a":["person","robot"],"name":"gitbot","title":"Git","uri":"https:\/\/zotonic.com\/id\/336"},"modified":"2026-11-11T14:07:03Z","title_slug":"controller_api"},"uri":"https:\/\/zotonic.com\/id\/1875","uri_template":"https:\/\/zotonic.com\/id\/:id","version":3514,"websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/1875"}},"status":"ok"}